# coding: utf-8

from datetime import timedelta
from urllib.parse import ParseResult, parse_qsl, urlencode, urlparse

from django.conf import settings
from django.contrib.auth.tokens import default_token_generator
from django.template import loader
from django.template.loader import render_to_string as render
from django.utils.timezone import now
from django.utils.translation import activate, get_language

from procu.api import models
from procu.api.enums import QR, QS
from .message import EmailMessage
from .utils import get_addrs, to_dict


def get_recipients(supplier_id):
    return models.User.objects.filter(supplier_id=supplier_id).values(
        'id', 'email', 'is_cold'
    )


def get_ref(quote_id, user_id):
    instance, created = models.QuoteRef.objects.get_or_create(
        quote_id=quote_id, user_id=user_id, defaults={'counter': 0}
    )
    return instance.ref


# ------------------------------------------------------------------------------


def notify_enquiry_published(quote_id, user):

    quote = models.Quote.objects.select_related(
        'request', 'request__address', 'request__legal_entity'
    ).get(id=quote_id)

    rfx = quote.request

    products = list(
        models.EnquiryProduct.objects.filter(request=rfx).values(
            'id', 'name', 'qty', 'comment'
        )
    )

    attachments = models.Attachment.objects.filter(request_id=rfx.id).values(
        'id', 'filename'
    )

    # --------------------------------------------------------------------------

    context = {
        'quote': to_dict(quote, {'id', 'deadline_at'}),
        'enquiry': to_dict(
            rfx, {'key', 'subject', 'description', 'no_replacement'}
        ),
        'address': to_dict(rfx.address, {'text'}),
        'legal_entity': to_dict(rfx.legal_entity, {'title'}),
        'products': products,
        'attachments': attachments,
        'user': to_dict(user, {'first_name', 'full_signature'}),
    }

    # ----------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(quote.supplier_id)

    subject = render('ext/enquiry_published/subject', context)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient

        body = render('ext/enquiry_published/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_enquiry_changed(quote_id, user, highlights):

    quote = models.Quote.objects.get(id=quote_id)

    rfx = models.Request.objects.select_related('address', 'legal_entity').get(
        quotes=quote_id
    )

    # --------------------------------------------------------------------------
    # Products

    products = list(
        models.EnquiryProduct.objects.filter(request=rfx).values(
            'id', 'name', 'qty', 'comment'
        )
    )

    hl = highlights.get('products', {})
    for product in products:
        product['highlights'] = hl.get(product['id'], False)

    # --------------------------------------------------------------------------
    # Attachments

    attachments = list(
        models.Attachment.objects.filter(enquiry_id=rfx.enquiry_id).values(
            'id', 'filename'
        )
    )

    hl = highlights.get('attachments', set())
    for a in attachments:
        a['highlighted'] = a['id'] in hl

    # --------------------------------------------------------------------------

    context = {
        'quote': to_dict(quote, {'id', 'deadline_at', 'has_offer'}),
        'enquiry': to_dict(
            rfx, {'key', 'subject', 'description', 'no_replacement'}
        ),
        'address': to_dict(rfx.address, {'text'}),
        'legal_entity': to_dict(rfx.legal_entity, {'title'}),
        'products': products,
        'attachments': attachments,
        'highlights': highlights,
    }

    context['quote']['is_ongoing'] = quote.status == QS.BIDDING

    # ----------------------------------------------------------------------

    activate('ru')

    recipients = get_recipients(quote.supplier_id)

    subject = render('ext/enquiry_changed/subject', context)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient
        body = render('ext/enquiry_changed/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_checkout(quote_id, user, products):

    quote = models.Quote.objects.get(id=quote_id)

    rfx_context = models.Request.objects.values('key', 'subject').get(
        quotes=quote_id
    )

    context = {
        'quote': to_dict(quote, {'id'}),
        'enquiry': rfx_context,
        'products': products,
    }

    # --------------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(quote.supplier_id)

    subject = render('ext/quote_checkout/subject', context)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient
        body = render('ext/quote_checkout/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_declined(quote_id, user):

    quote = models.Quote.objects.get(id=quote_id)

    rfx_context = models.Request.objects.values('key', 'subject').get(
        quotes=quote_id
    )

    context = {'quote': to_dict(quote, {'id'}), 'enquiry': rfx_context}

    activate('ru')

    context['quote']['status'] = QS.i18n[quote.status]
    context['quote']['reason'] = QR.i18n[quote.reason]

    # --------------------------------------------------------------------------

    recipients = get_recipients(quote.supplier_id)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient

        subject = render('ext/quote_declined/subject', context)
        body = render('ext/quote_declined/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_updated(quote_id, user, diff):

    from collections import OrderedDict

    new_diff = OrderedDict()
    for field, values in diff.items():
        new_diff[field] = {'old': values['__old__'], 'new': values['__new__']}

    supplier_id = models.Quote.objects.values_list(
        'supplier_id', flat=True
    ).get(id=quote_id)

    rfx_context = models.Request.objects.values('key', 'subject').get(
        quotes=quote_id
    )

    context = {
        'quote': {'id': quote_id},
        'enquiry': rfx_context,
        'diff': new_diff,
    }

    # ----------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(supplier_id)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote_id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote_id, recipient['id'])
        context['recipient'] = recipient

        subject = render('ext/quote_state/subject', context)
        body = render('ext/quote_state/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_shipped(quote_id, user):

    quote = models.Quote.objects.get(id=quote_id)
    rfx = models.Request.objects.select_related('address').get(quotes=quote_id)

    context = {
        'quote': to_dict(quote, {'id'}),
        'enquiry': {'key': rfx.key, 'subject': rfx.subject},
        'address': to_dict(rfx.address, {'text', 'contacts', 'comment'}),
    }

    # ----------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(quote.supplier_id)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient

        # -----

        contacts = context['address']['contacts']

        for contact in contacts:

            name = contact['name']

            if isinstance(name, dict):
                contact['name'] = name.get(get_language(), '')

        # -----

        subject = render('ext/quote_shipped/subject', context)
        body = render('ext/quote_shipped/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_address_changed(quote_id, user):

    quote = models.Quote.objects.get(id=quote_id)
    rfx = models.Request.objects.select_related('address').get(quotes=quote_id)

    context = {
        'quote': to_dict(quote, {'id'}),
        'enquiry': to_dict(rfx, {'key', 'subject'}),
        'address': to_dict(rfx.address, {'text', 'contacts', 'comment'}),
    }

    # ----------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(quote.supplier_id)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient

        # -----

        contacts = context['address']['contacts']

        for contact in contacts:

            name = contact['name']

            if isinstance(name, dict):
                contact['name'] = name.get(get_language(), '')

        # -----

        subject = render('ext/quote_address_changed/subject', context)
        body = render('ext/quote_address_changed/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


def notify_quote_comment(quote_comment_id, user, notify_from=None):

    comment = (
        models.QuoteComment.objects.select_related('author', 'quote')
        .prefetch_related('attachments')
        .get(id=quote_comment_id)
    )
    quote = comment.quote

    rfx_context = models.Request.objects.values('key', 'subject').get(
        quotes=comment.quote_id
    )

    context = {
        'quote': {'id': comment.quote_id},
        'enquiry': rfx_context,
        'comment': to_dict(comment, {'id', 'message', 'created_at'}),
        'author': to_dict(comment.author, {'full_name'}),
        'attachments': [
            to_dict(obj, {'id', 'filename'})
            for obj in comment.attachments.all()
        ],
    }

    # --------------------------------------------------------------------------

    activate('ru')
    recipients = get_recipients(quote.supplier_id)

    if notify_from is not None:
        recipients = filter(lambda x: x['id'] in notify_from, recipients)

    addrs = get_addrs(name=user.full_name, email=user.email, quote_id=quote.id)

    messages = []

    for recipient in recipients:

        context['token'] = get_ref(quote.id, recipient['id'])
        context['recipient'] = recipient

        subject = render('ext/quote_comment/subject', context)
        body = render('ext/quote_comment/body.html', context)

        messages.append(
            EmailMessage(
                to=[recipient['email']], subject=subject, body=body, **addrs
            )
        )

    return messages


# ------------------------------------------------------------------------------


def add_token_to_url(url, token):

    default = urlparse(settings.FRONT_EXTERNAL_PREFIX)
    parts = urlparse(url)

    if parts.netloc != default.netloc:
        parts = default

    query_list = parse_qsl(parts.query)
    query_list.append((settings.URL_PARAM_AUTH, token))

    parts = {f: getattr(parts, f) for f in parts._fields}

    parts['query'] = urlencode(query_list)

    return ParseResult(**parts).geturl()


def notify_login(user, retpath=None):

    later_than = now() - timedelta(minutes=settings.URL_AUTH_EXPIRE_IN)

    auth, created = models.AuthToken.objects.get_or_create(
        user_id=user.id, created_at__gt=later_than
    )

    if retpath is None:
        retpath = settings.FRONT_EXTERNAL_PREFIX

    url = add_token_to_url(retpath, auth.token)

    context = {
        'user': to_dict(user, {'id', 'email'}),
        'url': url,
        'expire_in': settings.URL_AUTH_EXPIRE_IN,
    }

    subject = loader.render_to_string('ext/login/subject')
    body = loader.render_to_string('ext/login/body.html', context)

    message = EmailMessage(
        to=[user.email],
        subject=subject,
        body=body,
        **get_addrs('Яндекс.Закупки'),
    )

    return [message]


# ------------------------------------------------------------------------------


def _notify_password_change(user, *, type_):

    token = default_token_generator.make_token(user)

    context = {
        'user': to_dict(user, {'id', 'email'}),
        'token': token,
        'timeout': settings.PASSWORD_RESET_TIMEOUT_DAYS,
    }

    subject = loader.render_to_string('ext/%s/subject' % type_)
    body = loader.render_to_string('ext/%s/body.html' % type_, context)

    message = EmailMessage(
        to=[user.email],
        subject=subject,
        body=body,
        **get_addrs('Яндекс.Закупки'),
    )

    return [message]


def notify_new_user(user):
    return _notify_password_change(user, type_='welcome')


def notify_reset(user):
    return _notify_password_change(user, type_='reset')
