"""
Django Celery tasks for service status app
"""

import logging
from smtplib import SMTPException

import requests
import simplejson
from celery import Task, shared_task
from celery.states import FAILURE
from django.conf import settings
from django.core.mail import EmailMessage
from edx_django_utils.monitoring import set_code_owner_attribute

from common.djangoapps.edxmako.shortcuts import render_to_string
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers

log = logging.getLogger(__name__)


class BaseSoftwareSecureTask(Task):  # lint-amnesty, pylint: disable=abstract-method
    """
    Base task class for use with Software Secure request.

    Permits updating information about user attempt in correspondence to submitting
    request to software secure.
    """
    abstract = True

    def on_success(self, retval, task_id, args, kwargs):
        """
        Update SoftwareSecurePhotoVerification object corresponding to this
        task with info about success.

        Updates user verification attempt to "submitted" if the response was ok otherwise
        set it to "must_retry".

        Assumes `retval` is a dict containing the task's result, with the following keys:
            'response_ok': boolean, indicating if the response was ok
            'response_text': string, indicating the response text in case of failure.
        """
        from .models import SoftwareSecurePhotoVerification

        user_verification = SoftwareSecurePhotoVerification.objects.get(id=kwargs['user_verification_id'])
        if retval['response_ok']:
            user_verification.mark_submit()
            log.info(
                'Sent request to Software Secure for user: %r and receipt ID %r.',
                user_verification.user.username,
                user_verification.receipt_id,
            )
            return user_verification

        user_verification.mark_must_retry(retval['response_text'])

    def after_return(self, status, retval, task_id, args, kwargs, einfo):
        """
        If max retries have reached and task status is still failing, mark user submission
        with "must_retry" so that it can be retried latter.
        """
        if self.max_retries == self.request.retries and status == FAILURE:
            from .models import SoftwareSecurePhotoVerification

            user_verification_id = kwargs['user_verification_id']
            user_verification = SoftwareSecurePhotoVerification.objects.get(id=user_verification_id)
            user_verification.mark_must_retry()
            log.error(
                'Software Secure submission failed for user %r, setting status to must_retry',
                user_verification.user.username,
                exc_info=True
            )


@shared_task
@set_code_owner_attribute
def send_verification_status_email(context):
    """
    Spins a task to send verification status email to the learner
    """
    subject = context.get('subject')
    message = render_to_string(context.get('template'), context.get('email_vars'))
    from_addr = configuration_helpers.get_value(
        'email_from_address',
        settings.DEFAULT_FROM_EMAIL
    )
    dest_addr = context.get('email')

    try:
        msg = EmailMessage(subject, message, from_addr, [dest_addr])
        msg.content_subtype = 'html'
        msg.send(fail_silently=False)
    except SMTPException:
        log.warning(u"Failure in sending verification status e-mail to %s", dest_addr)


@shared_task(
    base=BaseSoftwareSecureTask,
    bind=True,
    default_retry_delay=settings.SOFTWARE_SECURE_REQUEST_RETRY_DELAY,
    max_retries=settings.SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS,
)
@set_code_owner_attribute
def send_request_to_ss_for_user(self, user_verification_id, copy_id_photo_from):
    """
    Assembles a submission to Software Secure.

    Keyword Arguments:
        user_verification_id (int) SoftwareSecurePhotoVerification model object identifier.
        copy_id_photo_from (SoftwareSecurePhotoVerification): If provided, re-send the ID photo
                data from this attempt.  This is used for re-verification, in which new face photos
                are sent with previously-submitted ID photos.
    Returns:
        request.Response
    """
    from .models import SoftwareSecurePhotoVerification

    user_verification = SoftwareSecurePhotoVerification.objects.get(id=user_verification_id)
    log.info('=>New Verification Task Received %r', user_verification.user.username)
    try:
        headers, body = user_verification.create_request(copy_id_photo_from)
        # checkout PROD-1395 for detail why we are adding system certificate paths for verification.
        response = requests.post(
            settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_URL"],
            headers=headers,
            data=simplejson.dumps(body, indent=2, sort_keys=True, ensure_ascii=False).encode('utf-8'),
            verify=settings.VERIFY_STUDENT["SOFTWARE_SECURE"]['CERT_VERIFICATION_PATH']
        )
        return {
            'response_ok': getattr(response, 'ok', False),
            'response_text': getattr(response, 'text', '')
        }
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            (
                'Retrying sending request to Software Secure for user: %r, Receipt ID: %r '
                'attempt#: %s of %s'
            ),
            user_verification.user.username,
            user_verification.receipt_id,
            self.request.retries,
            settings.SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS,
        )
        log.error(str(exc))
        self.retry()
