"""
This module contains signals related to enterprise.
"""


import logging

import six
from django.conf import settings
from django.contrib.auth.models import User  # lint-amnesty, pylint: disable=imported-auth-user
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomer, EnterpriseCustomerUser
from integrated_channels.integrated_channel.tasks import (
    transmit_single_learner_data,
    transmit_single_subsection_learner_data
)
from slumber.exceptions import HttpClientError

from lms.djangoapps.email_marketing.tasks import update_user
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.djangoapps.signals.signals import COURSE_GRADE_NOW_PASSED, COURSE_ASSESSMENT_GRADE_CHANGED
from openedx.features.enterprise_support.api import enterprise_enabled
from openedx.features.enterprise_support.tasks import clear_enterprise_customer_data_consent_share_cache
from openedx.features.enterprise_support.utils import clear_data_consent_share_cache, is_enterprise_learner
from common.djangoapps.student.signals import UNENROLL_DONE

log = logging.getLogger(__name__)


@receiver(post_save, sender=EnterpriseCustomerUser)
def update_email_marketing_user_with_enterprise_vars(sender, instance, **kwargs):  # pylint: disable=unused-argument, invalid-name
    """
    Update the SailThru user with enterprise-related vars.
    """
    user = User.objects.get(id=instance.user_id)

    # perform update asynchronously
    update_user.delay(
        sailthru_vars={
            'is_enterprise_learner': True,
            'enterprise_name': instance.enterprise_customer.name,
        },
        email=user.email
    )


@receiver(post_save, sender=EnterpriseCourseEnrollment)
def update_dsc_cache_on_course_enrollment(sender, instance, **kwargs):  # pylint: disable=unused-argument
    """
        clears data_sharing_consent_needed cache after Enterprise Course Enrollment
    """
    clear_data_consent_share_cache(
        instance.enterprise_customer_user.user_id,
        instance.course_id
    )


@receiver(pre_save, sender=EnterpriseCustomer)
def update_dsc_cache_on_enterprise_customer_update(sender, instance, **kwargs):
    """
        clears data_sharing_consent_needed cache after enable_data_sharing_consent flag is changed.
    """
    old_instance = sender.objects.filter(pk=instance.uuid).first()
    if old_instance:   # instance already exists, so it's updating.
        new_value = instance.enable_data_sharing_consent
        old_value = old_instance.enable_data_sharing_consent
        if new_value != old_value:
            kwargs = {'enterprise_customer_uuid': six.text_type(instance.uuid)}
            result = clear_enterprise_customer_data_consent_share_cache.apply_async(kwargs=kwargs)
            log.info(u"DSC: Created {task_name}[{task_id}] with arguments {kwargs}".format(
                task_name=clear_enterprise_customer_data_consent_share_cache.name,
                task_id=result.task_id,
                kwargs=kwargs,
            ))


@receiver(COURSE_GRADE_NOW_PASSED, dispatch_uid="new_passing_enterprise_learner")
def handle_enterprise_learner_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a learner passing a course, transmit data to relevant integrated channel
    """
    if enterprise_enabled() and is_enterprise_learner(user):
        kwargs = {
            'username': six.text_type(user.username),
            'course_run_id': six.text_type(course_id)
        }

        transmit_single_learner_data.apply_async(kwargs=kwargs)


@receiver(COURSE_ASSESSMENT_GRADE_CHANGED)
def handle_enterprise_learner_subsection(sender, user, course_id, subsection_id, subsection_grade, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for an enterprise learner completing a subsection, transmit data to relevant integrated channel.
    """
    if enterprise_enabled() and is_enterprise_learner(user):
        kwargs = {
            'username': str(user.username),
            'course_run_id': str(course_id),
            'subsection_id': str(subsection_id),
            'grade': str(subsection_grade),
        }

        transmit_single_subsection_learner_data.apply_async(kwargs=kwargs)


@receiver(UNENROLL_DONE)
def refund_order_voucher(sender, course_enrollment, skip_refund=False, **kwargs):  # pylint: disable=unused-argument
    """
        Call the /api/v2/enterprise/coupons/create_refunded_voucher/ API to create new voucher and assign it to user.
    """

    if skip_refund:
        return
    if not course_enrollment.refundable():
        return
    if not EnterpriseCourseEnrollment.objects.filter(
        enterprise_customer_user__user_id=course_enrollment.user_id,
        course_id=str(course_enrollment.course.id)
    ).exists():
        return

    service_user = User.objects.get(username=settings.ECOMMERCE_SERVICE_WORKER_USERNAME)
    client = ecommerce_api_client(service_user)
    order_number = course_enrollment.get_order_attribute_value('order_number')
    if order_number:
        error_message = u"Encountered {} from ecommerce while creating refund voucher. Order={}, enrollment={}, user={}"
        try:
            client.enterprise.coupons.create_refunded_voucher.post({"order": order_number})
        except HttpClientError as ex:
            log.info(
                error_message.format(type(ex).__name__, order_number, course_enrollment, course_enrollment.user)
            )
        except Exception as ex:  # pylint: disable=broad-except
            log.exception(
                error_message.format(type(ex).__name__, order_number, course_enrollment, course_enrollment.user)
            )