import datetime
import itertools
import os

import post_office.mail
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.models import Q, F
from django.urls import reverse

import tracker.viewutil as viewutil
from tracker.models import Prize, PrizeWinner

AuthUser = get_user_model()


def _readtemplate(filename):
    with open(
        os.path.join(os.path.dirname(__file__), 'templates/tracker/email', filename),
        'r',
    ) as infile:
        return infile.read()


def get_event_default_sender_email(event):
    if event and event.prizecoordinator:
        return event.prizecoordinator.email
    else:
        return viewutil.get_default_email_from_user()


def event_sender_replyto_defaults(event, sender=None, replyTo=None):
    if sender is None:
        sender = get_event_default_sender_email(event)
    if replyTo is None:
        replyTo = sender
    return sender, replyTo


def prize_winners_with_email_pending(event):
    return PrizeWinner.objects.filter(
        prize__event=event, pendingcount__gt=0, emailsent=False
    )


def default_prize_winner_template_name():
    return getattr(
        settings, 'PRIZE_WINNER_EMAIL_TEMPLATE_NAME', 'default_prize_winner_template'
    )


def default_prize_winner_template():
    return post_office.models.EmailTemplate(
        name=default_prize_winner_template_name(),
        subject='{% if multi %}You won some prizes at {{ event.name }}{% else %}You won a prize at {{ event.name }}{% endif %}',
        description="""A basic template for automailing prize winners. DO NOT USE THIS TEMPLATE. Copy the contents and modify it to suit your needs.

The variables that will be defined are:
event -- the event for the set of prizes
winner -- the winner donor object
prizes -- the list of prizes this donor has won
accept_deadline -- the date by which we need a response or we will re-roll
prize_wins -- the list of PrizeWinner objects for this donor (if you need the additional information)
requires_shipping -- true if any of the prizes require shipping information
multi -- true if there are multiple prizes, false if there is only one (used for plurality branches)
prize_count -- the number of prizes won
reply_address -- the reply address specified on the form (will be overridden if the event has a prize coordinator)
""",
        html_content=_readtemplate('default_prize_winner_template.html'),
    )


def automail_prize_winners(
    event,
    prizeWinners,
    mailTemplate,
    sender=None,
    replyTo=None,
    domain=settings.DOMAIN,
    verbosity=0,
    dry_run=False,
):
    sender, replyTo = event_sender_replyto_defaults(event, sender, replyTo)

    winnerDict = {}
    for prizeWinner in prizeWinners:
        if prizeWinner.winner.id in list(winnerDict.keys()):
            winList = winnerDict[prizeWinner.winner.id]
        else:
            winList = []
            winnerDict[prizeWinner.winner.id] = winList
        winList.append(prizeWinner)
    for winnerk, prizesWon in winnerDict.items():
        winner = prizesWon[0].winner
        prizesList = []
        minAcceptDeadline = min(
            itertools.chain(
                [
                    x
                    for x in [pw.accept_deadline_date() for pw in prizesWon]
                    if x is not None
                ],
                [datetime.date.max],
            )
        )

        for prizeWon in prizesWon:
            prizesList.append(prizeWon.prize)
        formatContext = {
            'event': event,
            'winner': winner,
            'prize_wins': prizesWon,
            'multi': len(prizesWon) > 1,
            'prize_count': len(prizesWon),
            'reply_address': replyTo,
            'accept_deadline': minAcceptDeadline,
        }

        if not dry_run:
            post_office.mail.send(
                recipients=[winner.email],
                sender=sender,
                template=mailTemplate,
                context=formatContext,
                headers={'Reply-to': replyTo},
            )

        message = 'Mailed donor {0} for prize wins {1}'.format(
            winner.id, list([pw.id for pw in prizesWon])
        )

        if verbosity > 0:
            print(message)
        if not dry_run:
            viewutil.tracker_log('prize', message, event)
            for prizeWon in prizesWon:
                prizeWon.emailsent = True
                prizeWon.save()


def prizes_with_submission_email_pending(event):
    return Prize.objects.filter(
        Q(state='ACCEPTED') | Q(state='DENIED'), acceptemailsent=False, event=event
    )


def default_activate_prize_handlers_template_name():
    return getattr(
        settings,
        'ACTIVATE_PRIZE_HANDLER_EMAIL_TEMPLATE_NAME',
        'default_activate_prize_handler_template',
    )


def default_activate_prize_handlers_template():
    return post_office.models.EmailTemplate(
        name=default_activate_prize_handlers_template_name(),
        subject='{{ event.name }} Prize Contributor Account Activation',
        description="""A template to automail prize handlers to activate their account on the site, such that they can deal with prize accept/shipping stuff. Copy the contents and modify it to suit your needs.

The variables that will be defined are:
event -- the event object
handler -- the User objects of the person responsible for shipping
register_url -- the user registration URL (i.e. /user/register)
prize_set -- the set of prizes they are responsible
prize_count -- the number of prizes in the set
reply_address -- the address to reply to
""",
        html_content=_readtemplate('default_activate_prize_handlers.html'),
    )


def automail_inactive_prize_handlers(
    event,
    inactiveUsers,
    mailTemplate,
    sender=None,
    replyTo=None,
    domain=settings.DOMAIN,
    verbosity=0,
    dry_run=False,
):
    sender, replyTo = event_sender_replyto_defaults(event, sender, replyTo)
    for inactiveUser in inactiveUsers:
        eventPrizes = list(
            Prize.objects.filter(handler=inactiveUser, event=event, state='ACCEPTED')
        )
        formatContext = {
            'event': event,
            'handler': inactiveUser,
            'register_url': domain + reverse('tracker:register'),
            'prize_set': eventPrizes,
            'prize_count': len(eventPrizes),
            'reply_address': replyTo,
        }
        if not dry_run:
            post_office.mail.send(
                recipients=[inactiveUser.email],
                sender=sender,
                template=mailTemplate,
                context=formatContext,
                headers={'Reply-to': replyTo},
            )
        message = 'Mailed prize handler {0} (#{1}) for account activation'.format(
            inactiveUser, inactiveUser.id
        )
        if verbosity > 0:
            print(message)
        if not dry_run:
            viewutil.tracker_log('prize', message, event)


def get_event_inactive_prize_handlers(event):
    return AuthUser.objects.filter(
        is_active=False, prize__event=event, prize__state='ACCEPTED'
    ).distinct()


def default_prize_contributor_template_name():
    return getattr(
        settings,
        'PRIZE_CONTRIBUTOR_EMAIL_TEMPLATE_NAME',
        'default_prize_contributor_template',
    )


def default_prize_contributor_template():
    return post_office.models.EmailTemplate(
        name=default_prize_contributor_template_name(),
        subject='{{ event.name }} Prize Contributor Notification',
        description="""A basic template for automailing back prize accept/reject notifications. DO NOT USE THIS TEMPLATE. Copy the contents and modify it to suit your needs.

The variables that will be defined are:
event -- the event object.
handler -- the User object of the person responsible for shipping
accepted_prizes -- A list of all accepted prizes.
denied_prizes  -- A list of all denied prizes.
reply_address -- The address to reply to on this e-mail
user_index_url -- the user index url (i.e. /user/index)
""",
        html_content=_readtemplate('default_prize_contributor.html'),
    )


def automail_prize_contributors(
    event,
    prizes,
    mailTemplate,
    domain=settings.DOMAIN,
    sender=None,
    replyTo=None,
    verbosity=0,
    dry_run=False,
):
    sender, replyTo = event_sender_replyto_defaults(event, sender, replyTo)

    handlerDict = {}
    for prize in prizes:
        if prize.handler:
            prizeList = handlerDict.setdefault(prize.handler, [])
            prizeList.append(prize)
    for handler, prizeList in handlerDict.items():
        formatContext = {
            'user_index_url': domain + reverse('tracker:user_index'),
            'event': event,
            'handler': handler,
            'accepted_prizes': list(
                [prize for prize in prizeList if prize.state == 'ACCEPTED']
            ),
            'denied_prizes': list(
                [prize for prize in prizeList if prize.state == 'DENIED']
            ),
            'reply_address': replyTo,
        }
        if not dry_run:
            post_office.mail.send(
                recipients=[handler.email],
                sender=sender,
                template=mailTemplate,
                context=formatContext,
                headers={'Reply-to': replyTo},
            )
        message = 'Mailed prize handler {0} for prizes {1}'.format(
            handler.id, list([p.id for p in prizeList])
        )
        if verbosity > 0:
            print(message)
        if not dry_run:
            viewutil.tracker_log('prize', message, event)
            for prize in prizeList:
                prize.acceptemailsent = True
                prize.save()


def prizes_with_winner_accept_email_pending(event):
    return PrizeWinner.objects.filter(
        Q(prize__event=event)
        & Q(prize__state='ACCEPTED')
        & Q(acceptcount__gt=F('acceptemailsentcount'))
    )


def default_prize_winner_accept_template_name():
    return getattr(
        settings,
        'WINNER_ACCEPT_EMAIL_TEMPLATE_NAME',
        'default_prize_winner_accept_template',
    )


def default_prize_winner_accept_template():
    return post_office.models.EmailTemplate(
        name=default_prize_winner_accept_template_name(),
        description="""A basic template for automailing when prizes are accepted by winners. DO NOT USE THIS TEMPLATE. Copy the contents and modify it to suit your needs.

The variables that will be defined are:
user_index_url -- the user index url (i.e. /user/index)
prize_wins -- the list of PrizeWinner objects that were accepted
prize_count -- the number of prizes in the list
handler -- the user that is handling shipping the prizes
event -- the event for the set of prizes
reply_address -- the address to reply to (will be overridden if the event has a prize coordinator)
""",
        subject='Your Prize{{ prize_count|pluralize }} {{ prize_count|pluralize:"Has:Have" }} Been Accepted',
        html_content=_readtemplate('default_prize_winner_accept.html'),
    )


def automail_winner_accepted_prize(
    event,
    prizeWinners,
    mailTemplate,
    domain=settings.DOMAIN,
    sender=None,
    replyTo=None,
    verbosity=0,
    dry_run=False,
):
    sender, replyTo = event_sender_replyto_defaults(event, sender, replyTo)

    handlerDict = {}
    for prizeWinner in prizeWinners:
        if prizeWinner.prize.handler:
            prizeList = handlerDict.setdefault(prizeWinner.prize.handler, [])
            prizeList.append(prizeWinner)
    for handler, prizeList in handlerDict.items():
        formatContext = {
            'user_index_url': domain + reverse('tracker:user_index'),
            'prize_wins': prizeList,
            'prize_count': len(prizeList),
            'handler': handler,
            'event': event,
            'reply_address': replyTo,
        }
        if not dry_run:
            post_office.mail.send(
                recipients=[handler.email],
                sender=sender,
                template=mailTemplate,
                context=formatContext,
                headers={'Reply-to': replyTo},
            )
        message = 'Mailed handler {0} for prize accepts {1}'.format(
            handler.id, list([pw.id for pw in prizeList])
        )
        if verbosity > 0:
            print(message)
        if not dry_run:
            viewutil.tracker_log('prize', message, event)
            for prizeWinner in prizeList:
                prizeWinner.acceptemailsentcount = prizeWinner.acceptcount
                prizeWinner.save()


def prizes_with_shipping_email_pending(event):
    return PrizeWinner.objects.filter(
        Q(prize__event=event) & Q(shippingstate='SHIPPED') & Q(shippingemailsent=False)
    )


def default_prize_shipping_template_name():
    return getattr(
        settings, 'SHIPPING_EMAIL_TEMPLATE_NAME', 'default_prize_shipping_template'
    )


def default_prize_shipping_template():
    return post_office.models.EmailTemplate(
        name=default_prize_shipping_template_name(),
        description="""A basic template for automailing when prizes are shipped or keys are awarded. DO NOT USE THIS TEMPLATE. Copy the contents and modify it to suit your needs.

The variables that will be defined are:
prize_wins -- the list PrizeWinner objects
prize_count -- the number of prizes in the list
winner -- the donor that won the prizes
event -- the event for the set of prizes
reply_address -- the address to reply to (will be overridden if the event has a prize coordinator)
""",
        subject='Prize{{ prize_count|pluralize }} Shipped',
        html_content=_readtemplate('default_prize_shipping.html'),
    )


def automail_shipping_email_notifications(
    event,
    prizeWinners,
    mailTemplate,
    domain=settings.DOMAIN,
    sender=None,
    replyTo=None,
    verbosity=0,
    dry_run=False,
):
    sender, replyTo = event_sender_replyto_defaults(event, sender, replyTo)

    winnerDict = {}
    for prizeWinner in prizeWinners:
        prizeList = winnerDict.setdefault(prizeWinner.winner, [])
        prizeList.append(prizeWinner)
    for winner, prizeList in winnerDict.items():
        formatContext = {
            'prize_wins': prizeList,
            'prize_count': len(prizeList),
            'winner': winner,
            'event': event,
            'reply_address': replyTo,
        }
        if not dry_run:
            post_office.mail.send(
                recipients=[winner.email],
                sender=sender,
                template=mailTemplate,
                context=formatContext,
                headers={'Reply-to': replyTo},
            )
        message = 'Mailed donor {0} for prizes shipped {1}'.format(
            winner.id, list([pw.id for pw in prizeList])
        )
        if verbosity > 0:
            print(message)
        if not dry_run:
            viewutil.tracker_log('prize', message, event)
            for prizeWinner in prizeList:
                prizeWinner.shippingemailsent = True
                prizeWinner.save()