import logging
from datetime import timedelta

import pytz
from django.contrib import messages
from django.db.models import Q
from django.http import Http404
from django.shortcuts import redirect
from django.utils.formats import date_format
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, ListView

from pretix.base.i18n import LazyI18nString, language
from pretix.base.models import InvoiceAddress, LogEntry, Order
from pretix.base.models.event import SubEvent
from pretix.base.services.mail import SendMailException, mail
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.multidomain.urlreverse import build_absolute_uri

from . import forms

logger = logging.getLogger('pretix.plugins.sendmail')


class SenderView(EventPermissionRequiredMixin, FormView):
    template_name = 'pretixplugins/sendmail/send_form.html'
    permission = 'can_change_orders'
    form_class = forms.MailForm

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['event'] = self.request.event
        if 'from_log' in self.request.GET:
            try:
                from_log_id = self.request.GET.get('from_log')
                logentry = LogEntry.objects.get(
                    id=from_log_id,
                    event=self.request.event,
                    action_type='pretix.plugins.sendmail.sent'
                )
                kwargs['initial'] = {
                    'message': LazyI18nString(logentry.parsed_data['message']),
                    'subject': LazyI18nString(logentry.parsed_data['subject']),
                    'sendto': logentry.parsed_data['sendto'],
                }
                if logentry.parsed_data.get('subevent'):
                    try:
                        kwargs['initial']['subevent'] = self.request.event.subevents.get(
                            pk=logentry.parsed_data['subevent']['id']
                        )
                    except SubEvent.DoesNotExist:
                        pass
            except LogEntry.DoesNotExist:
                raise Http404(_('You supplied an invalid log entry ID'))
        return kwargs

    def form_invalid(self, form):
        messages.error(self.request, _('We could not send the email. See below for details.'))
        return super().form_invalid(form)

    def form_valid(self, form):
        qs = Order.objects.filter(event=self.request.event)
        statusq = Q(status__in=form.cleaned_data['sendto'])
        if 'overdue' in form.cleaned_data['sendto']:
            statusq |= Q(status=Order.STATUS_PENDING, expires__lt=now())
        orders = qs.filter(statusq)
        if form.cleaned_data.get('subevent'):
            orders = orders.filter(positions__subevent__in=(form.cleaned_data.get('subevent'),)).distinct()

        tz = pytz.timezone(self.request.event.settings.timezone)

        failures = []
        self.output = {}
        if not orders:
            messages.error(self.request, _('There are no orders matching this selection.'))
            return self.get(self.request, *self.args, **self.kwargs)

        if self.request.POST.get("action") == "preview":
            for l in self.request.event.settings.locales:

                with language(l):

                    context_dict = {
                        'code': 'ORDER1234',
                        'event': self.request.event.name,
                        'date': date_format(now(), 'SHORT_DATE_FORMAT'),
                        'expire_date': date_format(now() + timedelta(days=7), 'SHORT_DATE_FORMAT'),
                        'url': build_absolute_uri(self.request.event, 'presale:event.order', kwargs={
                            'order': 'ORDER1234',
                            'secret': 'longrandomsecretabcdef123456'
                        }),
                        'invoice_name': _('John Doe'),
                        'invoice_company': _('Sample Company LLC')
                    }

                    self.output[l] = []

                    subject = form.cleaned_data['subject'].localize(l)
                    preview_subject = subject.format_map(context_dict)
                    self.output[l].append(
                        _('Subject: {subject}').format(subject=preview_subject))

                    message = form.cleaned_data['message'].localize(l)
                    preview_text = message.format_map(context_dict)
                    self.output[l].append(preview_text)

            return self.get(self.request, *self.args, **self.kwargs)

        for o in orders:
            try:
                invoice_name = o.invoice_address.name
                invoice_company = o.invoice_address.company
            except InvoiceAddress.DoesNotExist:
                invoice_name = ""
                invoice_company = ""
            try:
                with language(o.locale):
                    email_context = {
                        'event': o.event,
                        'code': o.code,
                        'date': date_format(o.datetime.astimezone(tz), 'SHORT_DATETIME_FORMAT'),
                        'expire_date': date_format(o.expires, 'SHORT_DATE_FORMAT'),
                        'url': build_absolute_uri(o.event, 'presale:event.order', kwargs={
                            'order': o.code,
                            'secret': o.secret
                        }),
                        'invoice_name': invoice_name,
                        'invoice_company': invoice_company,
                    }
                    mail(
                        o.email, form.cleaned_data['subject'], form.cleaned_data['message'],
                        email_context,
                        self.request.event, locale=o.locale, order=o)
                    o.log_action(
                        'pretix.plugins.sendmail.order.email.sent',
                        user=self.request.user,
                        data={
                            'subject': form.cleaned_data['subject'].localize(o.locale),
                            'message': form.cleaned_data['message'].localize(o.locale).format_map(email_context),
                            'recipient': o.email
                        }
                    )
            except SendMailException:
                failures.append(o.email)
        self.request.event.log_action('pretix.plugins.sendmail.sent',
                                      user=self.request.user,
                                      data=dict(form.cleaned_data))
        if failures:
            messages.error(self.request,
                           _('Failed to send mails to the following users: {}'.format(' '.join(failures))))
        else:
            messages.success(self.request, _('Your message has been queued and will be sent to the selected users.'))

        return redirect(
            'plugins:sendmail:send',
            event=self.request.event.slug,
            organizer=self.request.event.organizer.slug
        )

    def get_context_data(self, *args, **kwargs):
        ctx = super().get_context_data(*args, **kwargs)
        ctx['output'] = getattr(self, 'output', None)
        return ctx


class EmailHistoryView(EventPermissionRequiredMixin, ListView):
    template_name = 'pretixplugins/sendmail/history.html'
    permission = 'can_change_orders'
    model = LogEntry
    context_object_name = 'logs'
    paginate_by = 5

    def get_queryset(self):
        qs = LogEntry.objects.filter(
            event=self.request.event,
            action_type='pretix.plugins.sendmail.sent'
        )
        return qs

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data()

        status = dict(Order.STATUS_CHOICE)
        status['overdue'] = _('pending with payment overdue')
        for log in ctx['logs']:
            log.pdata = log.parsed_data
            log.pdata['locales'] = {}
            for locale, msg in log.pdata['message'].items():
                log.pdata['locales'][locale] = {
                    'message': msg,
                    'subject': log.pdata['subject'][locale]
                }
            log.pdata['sendto'] = [
                status[s] for s in log.pdata['sendto']
            ]
            if log.pdata.get('subevent'):
                try:
                    log.pdata['subevent_obj'] = self.request.event.subevents.get(pk=log.pdata['subevent']['id'])
                except SubEvent.DoesNotExist:
                    pass

        return ctx
