"""
Django admin page for course modes
"""
from django.conf import settings
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin

from pytz import timezone, UTC

from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys import InvalidKeyError

from util.date_utils import get_time_display
from xmodule.modulestore.django import modulestore
from course_modes.models import CourseMode, CourseModeExpirationConfig

# Technically, we shouldn't be doing this, since verify_student is defined
# in LMS, and course_modes is defined in common.
#
# Once we move the responsibility for administering course modes into
# the Course Admin tool, we can remove this dependency and expose
# verification deadlines as a separate Django model admin.
#
# The admin page will work in both LMS and Studio,
# but the test suite for Studio will fail because
# the verification deadline table won't exist.
from lms.djangoapps.verify_student import models as verification_models


class CourseModeForm(forms.ModelForm):

    class Meta(object):
        model = CourseMode
        fields = '__all__'

    COURSE_MODE_SLUG_CHOICES = (
        [(CourseMode.DEFAULT_MODE_SLUG, CourseMode.DEFAULT_MODE_SLUG)] +
        [(mode_slug, mode_slug) for mode_slug in CourseMode.VERIFIED_MODES] +
        [(CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.NO_ID_PROFESSIONAL_MODE)] +
        [(mode_slug, mode_slug) for mode_slug in CourseMode.CREDIT_MODES] +
        # need to keep legacy modes around for awhile
        [(CourseMode.DEFAULT_SHOPPINGCART_MODE_SLUG, CourseMode.DEFAULT_SHOPPINGCART_MODE_SLUG)]
    )

    mode_slug = forms.ChoiceField(choices=COURSE_MODE_SLUG_CHOICES, label=_("Mode"))

    # The verification deadline is stored outside the course mode in the verify_student app.
    # (we used to use the course mode expiration_datetime as both an upgrade and verification deadline).
    # In order to make this transition easier, we include the verification deadline as a custom field
    # in the course mode admin form.  Longer term, we will deprecate the course mode Django admin
    # form in favor of an external Course Administration Tool.
    verification_deadline = forms.SplitDateTimeField(
        label=_("Verification Deadline"),
        required=False,
        help_text=_(
            "OPTIONAL: After this date/time, users will no longer be able to submit photos for verification.  "
            "This appies ONLY to modes that require verification."
        ),
        widget=admin.widgets.AdminSplitDateTime,
    )

    def __init__(self, *args, **kwargs):
        super(CourseModeForm, self).__init__(*args, **kwargs)

        default_tz = timezone(settings.TIME_ZONE)

        if self.instance._expiration_datetime:  # pylint: disable=protected-access
            # django admin is using default timezone. To avoid time conversion from db to form
            # convert the UTC object to naive and then localize with default timezone.
            _expiration_datetime = self.instance._expiration_datetime.replace(  # pylint: disable=protected-access
                tzinfo=None
            )
            self.initial["_expiration_datetime"] = default_tz.localize(_expiration_datetime)
        # Load the verification deadline
        # Since this is stored on a model in verify student, we need to load it from there.
        # We need to munge the timezone a bit to get Django admin to display it without converting
        # it to the user's timezone.  We'll make sure we write it back to the database with the timezone
        # set to UTC later.
        if self.instance.course_id and self.instance.mode_slug in CourseMode.VERIFIED_MODES:
            deadline = verification_models.VerificationDeadline.deadline_for_course(self.instance.course_id)
            self.initial["verification_deadline"] = (
                default_tz.localize(deadline.replace(tzinfo=None))
                if deadline is not None else None
            )

    def clean_course_id(self):
        course_id = self.cleaned_data['course_id']
        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            try:
                course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
            except InvalidKeyError:
                raise forms.ValidationError("Cannot make a valid CourseKey from id {}!".format(course_id))

        if not modulestore().has_course(course_key):
            raise forms.ValidationError("Cannot find course with id {} in the modulestore".format(course_id))

        return course_key

    def clean__expiration_datetime(self):
        """
        Ensure that the expiration datetime we save uses the UTC timezone.
        """
        # django admin saving the date with default timezone to avoid time conversion from form to db
        # changes its tzinfo to UTC
        if self.cleaned_data.get("_expiration_datetime"):
            return self.cleaned_data.get("_expiration_datetime").replace(tzinfo=UTC)

    def clean_verification_deadline(self):
        """
        Ensure that the verification deadline we save uses the UTC timezone.
        """
        if self.cleaned_data.get("verification_deadline"):
            return self.cleaned_data.get("verification_deadline").replace(tzinfo=UTC)

    def clean(self):
        """
        Clean the form fields.
        This is the place to perform checks that involve multiple form fields.
        """
        cleaned_data = super(CourseModeForm, self).clean()
        mode_slug = cleaned_data.get("mode_slug")
        upgrade_deadline = cleaned_data.get("_expiration_datetime")
        verification_deadline = cleaned_data.get("verification_deadline")

        # Allow upgrade deadlines ONLY for the "verified" mode
        # This avoids a nasty error condition in which the upgrade deadline is set
        # for a professional education course before the enrollment end date.
        # When this happens, the course mode expires and students are able to enroll
        # in the course for free.  To avoid this, we explicitly prevent admins from
        # setting an upgrade deadline for any mode except "verified" (which has an upgrade path).
        if upgrade_deadline is not None and mode_slug != CourseMode.VERIFIED:
            raise forms.ValidationError(
                'Only the "verified" mode can have an upgrade deadline.  '
                'For other modes, please set the enrollment end date in Studio.'
            )

        # Verification deadlines are allowed only for verified modes
        if verification_deadline is not None and mode_slug not in CourseMode.VERIFIED_MODES:
            raise forms.ValidationError("Verification deadline can be set only for verified modes.")

        # Verification deadline must be after the upgrade deadline,
        # if an upgrade deadline is set.
        # There are cases in which we might want to set a verification deadline,
        # but not an upgrade deadline (for example, a professional education course that requires verification).
        if verification_deadline is not None:
            if upgrade_deadline is not None and verification_deadline < upgrade_deadline:
                raise forms.ValidationError("Verification deadline must be after the upgrade deadline.")

        return cleaned_data

    def save(self, commit=True):
        """
        Save the form data.
        """
        # Trigger validation so we can access cleaned data
        if self.is_valid():
            course_key = self.cleaned_data.get("course_id")
            verification_deadline = self.cleaned_data.get("verification_deadline")
            mode_slug = self.cleaned_data.get("mode_slug")

            # Since the verification deadline is stored in a separate model,
            # we need to handle saving this ourselves.
            # Note that verification deadline can be `None` here if
            # the deadline is being disabled.
            if course_key is not None and mode_slug in CourseMode.VERIFIED_MODES:
                verification_models.VerificationDeadline.set_deadline(course_key, verification_deadline)

        return super(CourseModeForm, self).save(commit=commit)


class CourseModeAdmin(admin.ModelAdmin):
    """Admin for course modes"""
    form = CourseModeForm

    fields = (
        'course_id',
        'mode_slug',
        'mode_display_name',
        'min_price',
        'currency',
        '_expiration_datetime',
        'verification_deadline',
        'sku',
        'bulk_sku'
    )

    search_fields = ('course_id',)

    list_display = (
        'id',
        'course_id',
        'mode_slug',
        'min_price',
        'expiration_datetime_custom',
        'sku',
        'bulk_sku'
    )

    def expiration_datetime_custom(self, obj):
        """adding custom column to show the expiry_datetime"""
        if obj.expiration_datetime:
            return get_time_display(obj.expiration_datetime, '%B %d, %Y, %H:%M  %p')

    # Display a more user-friendly name for the custom expiration datetime field
    # in the Django admin list view.
    expiration_datetime_custom.short_description = "Upgrade Deadline"


class CourseModeExpirationConfigAdmin(admin.ModelAdmin):
    """Admin interface for the course mode auto expiration configuration. """

    class Meta(object):
        model = CourseModeExpirationConfig

admin.site.register(CourseMode, CourseModeAdmin)
admin.site.register(CourseModeExpirationConfig, CourseModeExpirationConfigAdmin)