"""
CCX Enrollment operations for use by Coach APIs.

Does not include any access control, be sure to check access before calling.
"""
import logging
from courseware.courses import get_course_about_section  # pylint: disable=import-error
from courseware.courses import get_course_by_id  # pylint: disable=import-error
from django.contrib.auth.models import User
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.mail import send_mail
from edxmako.shortcuts import render_to_string  # pylint: disable=import-error
from microsite_configuration import microsite  # pylint: disable=import-error
from xmodule.modulestore.django import modulestore
from xmodule.error_module import ErrorDescriptor

from .models import (
    CcxMembership,
    CcxFutureMembership,
)
from .overrides import get_current_ccx


log = logging.getLogger("edx.ccx")


class EmailEnrollmentState(object):
    """ Store the complete enrollment state of an email in a class """
    def __init__(self, ccx, email):
        exists_user = User.objects.filter(email=email).exists()
        if exists_user:
            user = User.objects.get(email=email)
            ccx_member = CcxMembership.objects.filter(ccx=ccx, student=user)
            in_ccx = ccx_member.exists()
            full_name = user.profile.name
        else:
            user = None
            in_ccx = False
            full_name = None
        self.user = exists_user
        self.member = user
        self.full_name = full_name
        self.in_ccx = in_ccx

    def __repr__(self):
        return "{}(user={}, member={}, in_ccx={})".format(
            self.__class__.__name__,
            self.user,
            self.member,
            self.in_ccx,
        )

    def to_dict(self):
        """ return dict with membership and ccx info """
        return {
            'user': self.user,
            'member': self.member,
            'in_ccx': self.in_ccx,
        }


def enroll_email(ccx, student_email, auto_enroll=False, email_students=False, email_params=None):
    """
    Send email to newly enrolled student
    """
    if email_params is None:
        email_params = get_email_params(ccx, True)
    previous_state = EmailEnrollmentState(ccx, student_email)

    if previous_state.user:
        user = User.objects.get(email=student_email)
        if not previous_state.in_ccx:
            membership = CcxMembership(
                ccx=ccx, student=user, active=True
            )
            membership.save()
        elif auto_enroll:
            # activate existing memberships
            membership = CcxMembership.objects.get(student=user, ccx=ccx)
            membership.active = True
            membership.save()
        if email_students:
            email_params['message'] = 'enrolled_enroll'
            email_params['email_address'] = student_email
            email_params['full_name'] = previous_state.full_name
            send_mail_to_student(student_email, email_params)
    else:
        membership = CcxFutureMembership(
            ccx=ccx, auto_enroll=auto_enroll, email=student_email
        )
        membership.save()
        if email_students:
            email_params['message'] = 'allowed_enroll'
            email_params['email_address'] = student_email
            send_mail_to_student(student_email, email_params)

    after_state = EmailEnrollmentState(ccx, student_email)

    return previous_state, after_state


def unenroll_email(ccx, student_email, email_students=False, email_params=None):
    """
    send email to unenrolled students
    """
    if email_params is None:
        email_params = get_email_params(ccx, True)
    previous_state = EmailEnrollmentState(ccx, student_email)

    if previous_state.in_ccx:
        CcxMembership.objects.get(
            ccx=ccx, student=previous_state.member
        ).delete()
        if email_students:
            email_params['message'] = 'enrolled_unenroll'
            email_params['email_address'] = student_email
            email_params['full_name'] = previous_state.full_name
            send_mail_to_student(student_email, email_params)
    else:
        if CcxFutureMembership.objects.filter(
                ccx=ccx, email=student_email).exists():
            CcxFutureMembership.objects.get(
                ccx=ccx, email=student_email
            ).delete()
        if email_students:
            email_params['message'] = 'allowed_unenroll'
            email_params['email_address'] = student_email
            send_mail_to_student(student_email, email_params)

    after_state = EmailEnrollmentState(ccx, student_email)

    return previous_state, after_state


def get_email_params(ccx, auto_enroll, secure=True):
    """
    get parameters for enrollment emails
    """
    protocol = 'https' if secure else 'http'
    course_id = ccx.course_id

    stripped_site_name = microsite.get_value(
        'SITE_NAME',
        settings.SITE_NAME
    )
    registration_url = u'{proto}://{site}{path}'.format(
        proto=protocol,
        site=stripped_site_name,
        path=reverse('register_user')
    )
    course_url = u'{proto}://{site}{path}'.format(
        proto=protocol,
        site=stripped_site_name,
        path=reverse(
            'course_root',
            kwargs={'course_id': course_id.to_deprecated_string()}
        )
    )

    course_about_url = None
    if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
        course_about_url = u'{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse(
                'about_course',
                kwargs={'course_id': course_id.to_deprecated_string()}
            )
        )

    email_params = {
        'site_name': stripped_site_name,
        'registration_url': registration_url,
        'course': ccx,
        'auto_enroll': auto_enroll,
        'course_url': course_url,
        'course_about_url': course_about_url,
    }
    return email_params


def send_mail_to_student(student, param_dict):
    """
    Check parameters, set text template and send email to student
    """
    if 'course' in param_dict:
        param_dict['course_name'] = param_dict['course'].display_name

    param_dict['site_name'] = microsite.get_value(
        'SITE_NAME',
        param_dict['site_name']
    )

    subject = None
    message = None

    message_type = param_dict['message']

    email_template_dict = {
        'allowed_enroll': (
            'ccx/enroll_email_allowedsubject.txt',
            'ccx/enroll_email_allowedmessage.txt'
        ),
        'enrolled_enroll': (
            'ccx/enroll_email_enrolledsubject.txt',
            'ccx/enroll_email_enrolledmessage.txt'
        ),
        'allowed_unenroll': (
            'ccx/unenroll_email_subject.txt',
            'ccx/unenroll_email_allowedmessage.txt'
        ),
        'enrolled_unenroll': (
            'ccx/unenroll_email_subject.txt',
            'ccx/unenroll_email_enrolledmessage.txt'
        ),
    }

    subject_template, message_template = email_template_dict.get(
        message_type, (None, None)
    )
    if subject_template is not None and message_template is not None:
        subject = render_to_string(subject_template, param_dict)
        message = render_to_string(message_template, param_dict)

    if subject and message:
        message = message.strip()

        subject = ''.join(subject.splitlines())
        from_address = microsite.get_value(
            'email_from_address',
            settings.DEFAULT_FROM_EMAIL
        )

        send_mail(
            subject,
            message,
            from_address,
            [student],
            fail_silently=False
        )


def get_all_ccx_for_user(user):
    """return all CCXS to which the user is registered

    Returns a list of dicts: {
        ccx_name: <formatted title of CCX course>
        ccx_url: <url to view this CCX>
        ccx_active: True if this ccx is currently the 'active' one
        mooc_name: <formatted title of the MOOC course for this CCX>
        mooc_url: <url to view this MOOC>
    }
    """
    if user.is_anonymous():
        return []
    current_active_ccx = get_current_ccx()
    memberships = []
    for membership in CcxMembership.memberships_for_user(user):
        course = get_course_by_id(membership.ccx.course_id)
        ccx = membership.ccx
        ccx_title = ccx.display_name
        mooc_title = get_course_about_section(course, 'title')
        url = reverse(
            'switch_active_ccx',
            args=[course.id.to_deprecated_string(), membership.ccx.id]
        )
        mooc_url = reverse(
            'switch_active_ccx',
            args=[course.id.to_deprecated_string(), ]
        )
        memberships.append({
            'ccx_name': ccx_title,
            'ccx_url': url,
            'active': membership.ccx == current_active_ccx,
            'mooc_name': mooc_title,
            'mooc_url': mooc_url,
        })
    return memberships


def get_ccx_membership_triplets(user, course_org_filter, org_filter_out_set):
    """
    Get the relevant set of (CustomCourseForEdX, CcxMembership, Course)
    triplets to be displayed on a student's dashboard.
    """
    # only active memberships for now
    for membership in CcxMembership.memberships_for_user(user):
        ccx = membership.ccx
        store = modulestore()
        with store.bulk_operations(ccx.course_id):
            course = store.get_course(ccx.course_id)
            if course and not isinstance(course, ErrorDescriptor):
                # if we are in a Microsite, then filter out anything that is not
                # attributed (by ORG) to that Microsite
                if course_org_filter and course_org_filter != course.location.org:
                    continue
                # Conversely, if we are not in a Microsite, then let's filter out any enrollments
                # with courses attributed (by ORG) to Microsites
                elif course.location.org in org_filter_out_set:
                    continue

                yield (ccx, membership, course)
            else:
                log.error("User {0} enrolled in {2} course {1}".format(  # pylint: disable=logging-format-interpolation
                    user.username, ccx.course_id, "broken" if course else "non-existent"
                ))