# coding: utf-8

from collections import defaultdict

from django.db import connection
from django.db.models import Case, CharField, F, When
from django.template.loader import render_to_string as render
from django.utils.translation import activate

from procu.api import models
from procu.api.enums import ACCESS_SOURCE
from procu.api.push.utils import get_addrs, to_dict
from procu.api.utils.decimal import format_price
from .message import EmailMessage


def get_price(product):
    if product.value is None:
        return ''
    else:
        return '{}\u00A0{}\u00A0{}'.format(
            product.currency.prefix,
            format_price(product.value),
            product.currency.suffix,
        ).strip()


def get_recipients(enquiry, *, except_user=None) -> set:

    recipients = {enquiry.author}

    if enquiry.manager:
        recipients.add(enquiry.manager)

    accesses = enquiry.access.filter(
        is_subscribed=True, sources__contains=[ACCESS_SOURCE.ACCESS]
    )
    recipients.update(a.user for a in accesses)

    if except_user is not None:
        recipients.discard(except_user)

    return recipients


def get_emails_from_users(instances):
    return {u.email for u in instances}


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


def notify_quote_comment(quote_comment_id, user):

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

    quote = comment.quote
    enquiry = models.Enquiry.objects.get(request__quotes=quote.id)

    context = {
        'comment': to_dict(comment, {'id', 'message', 'created_at'}),
        'author': to_dict(
            comment.author, {'full_name', 'username', 'is_staff'}
        ),
        'attachments': [
            to_dict(obj, ('id', 'filename'))
            for obj in comment.attachments.all()
        ],
        'enquiry': to_dict(enquiry, {'key', 'subject'}),
        'quote': to_dict(quote, {'id'}),
        'supplier': to_dict(quote.supplier, {'title'}),
    }

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

    activate('ru')

    if user.is_staff:
        sender_name = user.full_name
    else:
        sender_name = quote.supplier.title

    addrs = get_addrs(sender_name, quote_id=quote.id)

    recipients = get_recipients(enquiry, except_user=user)

    for u in list(recipients):
        if 'api.list_quotecomment' not in quote.permissions(u):
            recipients.remove(u)

    emails = get_emails_from_users(recipients)

    messages = []

    for email in emails:

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

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

    return messages


def notify_enquiry_comment(enquiry_comment_id, user):

    comment = (
        models.EnquiryComment.objects.select_related('author', 'enquiry')
        .prefetch_related('invitees', 'attachments')
        .get(id=enquiry_comment_id)
    )

    context = {
        'comment': to_dict(comment, {'id', 'message', 'created_at'}),
        'author': to_dict(comment.author, {'username', 'full_name'}),
        'invitees': [
            to_dict(obj, ('username', 'full_name'))
            for obj in comment.invitees.all()
        ],
        'attachments': [
            to_dict(obj, ('id', 'filename'))
            for obj in comment.attachments.all()
        ],
        'enquiry': to_dict(comment.enquiry, {'key', 'subject'}),
    }

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

    activate('ru')

    invitees = {u.email for u in comment.invitees.all()}

    recipients = get_recipients(comment.enquiry, except_user=user)

    for u in list(recipients):
        if 'api.list_enquirycomment' not in comment.enquiry.permissions(u):
            recipients.remove(u)

    emails = get_emails_from_users(recipients)
    emails -= invitees

    messages = []

    sender_name = user.full_name
    addrs = get_addrs(sender_name)

    for email in emails:
        subject = render('int/enquiry_comment/subject', context)
        body = render('int/enquiry_comment/body.html', context)

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

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

    for invitee in invitees:

        subject = render('int/enquiry_comment_invited/subject', context)
        body = render('int/enquiry_comment_invited/body.html', context)

        messages.append(
            EmailMessage(to=[invitee], subject=subject, body=body, **addrs)
        )

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

    return messages


def notify_quote_products(quote_id, user):

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

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

    # Ensure prices are formatted according to the current language
    activate('ru')

    # Make a mapping of EnquiryProduct.id to a list of its QuoteProduct's

    offered_products = defaultdict(list)

    quote_products = (
        models.QuoteProduct.objects.prepared(
            tax=True, totals=False, rounded=False
        )
        .filter(quote_id=quote_id)
        .select_related('enquiry_product', 'currency')
        .annotate(
            display_name=Case(
                When(is_replacement=True, then=F('name')),
                default=F('enquiry_product__name'),
                output_field=CharField(),
            )
        )
        .order_by('enquiry_product__pk', 'is_replacement', 'pk')
    )

    for qp in quote_products:
        product = {
            'name': qp.display_name,
            'price': get_price(qp),
            'qty_expected': qp.enquiry_product.qty,
            **to_dict(
                qp, {'comment', 'delivery_time', 'qty', 'is_replacement'}
            ),
        }
        offered_products[qp.enquiry_product.id].append(product)

    # --------------------------------------------------------------------------
    # Fetch a list of EnquiryProduct's

    enquiry_products = (
        models.EnquiryProduct.objects.filter(request__enquiry_id=enquiry.id)
        .values('id', 'name')
        .order_by('pk')
    )

    # --------------------------------------------------------------------------
    # Compile the final list of products:

    products = []

    for ep in enquiry_products:
        ps = offered_products[ep['id']]

        if ps:
            if all(x['is_replacement'] for x in ps):
                products.append({'name': ep['name']})

            # append offered products if any,
            products.extend(ps)
        else:
            # otherwise add a dummy item to show the lack of offers
            products.append({'name': ep['name']})

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

    context = {
        'quote': to_dict(quote, {'id', 'terms'}),
        'supplier': to_dict(quote.supplier, {'title'}),
        'enquiry': to_dict(enquiry, {'key', 'subject'}),
        # -------------------
        'products': products,
    }

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

    recipients = get_recipients(enquiry)

    for u in list(recipients):
        if 'api.list_quotecomment' not in quote.permissions(u):
            recipients.remove(u)

    emails = get_emails_from_users(recipients)

    messages = []

    sender_name = quote.supplier.title
    addrs = get_addrs(sender_name, quote_id=quote.id)

    for email in emails:

        subject = render('int/quote_products/subject', context)
        body = render('int/quote_products/body.html', context)

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

    return messages


def notify_enquiry_expired(enquiry_id):

    enquiry = models.Enquiry.objects.select_related(
        'author', 'manager', 'request'
    ).get(id=enquiry_id)

    context = {
        'enquiry': to_dict(enquiry, {'key', 'subject'}),
        'request': to_dict(enquiry.request, {'deadline_at'}),
        'manager': (
            to_dict(enquiry.manager, {'username', 'full_name'})
            if enquiry.manager
            else None
        ),
    }

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

    quotes = models.Quote.objects.filter(
        request=enquiry.request, is_deleted=False, has_offer=True
    ).values('id', title=F('supplier__title'))

    context['quotes'] = list(quotes)

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

    activate('ru')

    recipients = get_recipients(enquiry)
    emails = get_emails_from_users(recipients)
    messages = []

    for email in emails:

        subject = render('int/enquiry_expired/subject', context)
        body = render('int/enquiry_expired/body.html', context)

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

    return messages


def notify_enquiry_assigned(enquiry_id, user):

    enquiry = models.Enquiry.objects.select_related('manager').get(
        id=enquiry_id
    )

    assert enquiry.manager

    context = {
        'enquiry': to_dict(enquiry, ['key', 'subject']),
        'user': to_dict(user, ['full_name']),
    }

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

    activate('ru')

    recipients = [enquiry.manager.email]

    subject = render('int/enquiry_assigned/subject', context)
    body = render('int/enquiry_assigned/body.html', context)

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

    return [message]


def notify_enquiry_created(enquiry_id, user):

    enquiry = models.Enquiry.objects.get(id=enquiry_id)

    context = {
        'enquiry': to_dict(enquiry, ['key', 'subject']),
        'user': to_dict(user, ['full_name', 'sex']),
    }

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

    activate('ru')

    recipients = set(
        enquiry.access.values_list('user__email', flat=True).filter(
            user__is_subscribed_created=True, user__is_staff=True
        )
    )

    # Add admins separately
    # as they may not be in the access table

    admins_query = """
    SELECT email FROM api_user AS u
        JOIN api_usergroup AS ug ON (u.username = ug.username)
        JOIN auth_group AS g ON (ug.group_id = g.id)
    WHERE u.is_deleted='f'
      AND u.is_staff='t'
      AND u.is_subscribed_created='t'
      AND g.name = 'group-admin';
    """

    with connection.cursor() as cursor:
        cursor.execute(admins_query)
        recipients.update(row[0] for row in cursor.fetchall())

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

    recipients.discard(user.email)

    subject = render('int/enquiry_created/subject', context)
    body = render('int/enquiry_created/body.html', context)

    messages = []

    for recipient in recipients:
        msg = EmailMessage(
            to=[recipient],
            subject=subject,
            body=body,
            **get_addrs('Яндекс.Закупки'),
        )
        messages.append(msg)

    return messages
