# -*- coding: utf-8 -*-
from datetime import date, timedelta, datetime
from collections import defaultdict
from urlparse import urljoin

from django.core.mail import get_connection
from django.utils import formats
from django.utils import translation
from django.conf import settings
from django.template import Context
from django.template.loader import get_template
from django.utils.translation import ugettext as _
from django.utils.translation import pgettext

from celery import shared_task

from fb.celery_app import app
from fb.staff.misc import get_heads_for_persons
from fb.staff.models import Person
from fb.common.lock import get_lock_or_do_nothing
from fb.common.email import EmailMessage

from itertools import groupby


def _format_from_address(username, real_address):
    return u'{username} ({service_name}) <{real_address}>'.format(
        username=username,
        service_name=_('common.service_name'),
        real_address=real_address,
    )


def _get_notifications(heads, persons_by_head, feedback_instance):
    from fb.quizz.models import PersonAnswer
    mail_template = get_template('new.html')
    for head in heads:
        with translation.override(head.language_ui):
            _persons = persons_by_head.get(head.staff_id)
            link = _create_link(_persons)
            quizz_answers = PersonAnswer.objects.filter(
                feedback=feedback_instance,
                person=feedback_instance.reporter,
            )
            quizz_answers_dict = {}
            for person in _persons:
                # group person answers by question
                person_answers = groupby(
                    quizz_answers.filter(on_whom=person).order_by('quizz_answer__question_id'),
                    key=lambda x: x.quizz_answer.question.pk,
                )
                # get answer groups and make shure that open answers placed in the end of each group
                person_answers = map(
                    lambda x: sorted(x[1], key=lambda y: y.quizz_answer.is_open),
                    person_answers
                )
                if person_answers:
                    quizz_answers_dict[person] = person_answers
            c = Context({
                'mail_template_language': head.language_ui,
                'head': head,
                'persons': _persons,
                'feedback_instance': feedback_instance,
                'quizz_answers': quizz_answers_dict,
                'link': link,
                'base_url': settings.BASE_URL,
            })
            from_email = _format_from_address(
                username=feedback_instance.reporter.get_full_name(),
                real_address=settings.EMAIL_HOST_USER,
            )
            if feedback_instance.is_changed:
                subject = _('changed.subject')
            else:
                subject = _('new.subject')
            body = mail_template.render(c)
            yield head, body, subject, from_email


def _create_link(suggested_persons, request_id=None):
    person_part = '?persons=' + ','.join(str(p.staff_id) for p in suggested_persons)
    if request_id:
        person_part += ('&request_id=' + str(request_id))
    base_url = settings.BASE_URL
    return urljoin(base_url, person_part)


def _get_feedback_request(who, suggested_persons, reason, request_ids):
    mail_template = get_template('request.html')
    from fb.feedback.models import FeedbackRequest
    target_requests = (
        FeedbackRequest.objects
        .select_related('suggest_to')
        .filter(id__in=request_ids)
    )
    for fb_request in target_requests:
        person = fb_request.suggest_to
        with translation.override(person.language_ui):
            link = _create_link(suggested_persons, fb_request.id)
            c = Context({
                'mail_template_language': person.language_ui,
                'who': who,
                'asked_person': person,
                'suggested_persons': suggested_persons,
                'reason': reason,
                'deadline': fb_request.deadline,
                'link': link,
                'base_url': settings.BASE_URL,
            })
            subject = _('request.subject')
            if fb_request.deadline:
                subject = ' '.join([
                    subject,
                    _('request.deadline'),
                    formats.date_format(fb_request.deadline),
                ])
            from_email = _format_from_address(
                username=who.get_full_name(),
                real_address=settings.EMAIL_HOST_USER,
            )
            body = mail_template.render(c)
        yield person, body, subject, from_email


def _send_notification(persons_by_head, person_ids, feedback_instance):
    head_ids = persons_by_head.keys()

    persons = Person.objects.filter(staff_id__in=person_ids).all()
    persons_by_id = {p.staff_id: p for p in persons}
    heads = Person.objects.filter(staff_id__in=head_ids).all()
    _persons_by_head = {}
    for _h, _ps in persons_by_head.iteritems():
        _persons_by_head[_h] = [persons_by_id.get(pid) for pid in _ps]

    notifications = _get_notifications(heads, _persons_by_head, feedback_instance)
    headers = {'Reply-To': feedback_instance.reporter.get_email()}

    messages = []
    for head, notification, subject, from_email in notifications:
        message = EmailMessage(
            subject=subject,
            body=notification,
            to=[head.get_email()],
            from_email=from_email,
            headers=headers,
        )
        messages.append(message)

    connection = get_connection(fail_silently=False)
    return connection.send_messages(messages)


@shared_task
def send_notification(instance_id):
    from fb.feedback.models import Feedback
    instance = Feedback.objects.get(id=instance_id)
    persons = instance.persons.values_list('staff_id', flat=True)
    blacklisted_persons = set(
        instance.blacklisted_persons.values_list('staff_id', flat=True)
    )
    heads = get_heads_for_persons(persons, all_heads=True)

    def _transpose(some_dict, blacklist):
        result = defaultdict(list)
        for person, _heads in some_dict.iteritems():
            for head in _heads:
                if head not in blacklist:
                    result[head].append(person)
                    break
        return result

    persons_by_head = _transpose(heads, blacklisted_persons)

    return _send_notification(persons_by_head, persons, instance)


@app.task(
    autoretry_for=(Exception,),
    retry_kwargs={'max_retries': 3, 'countdown': 2}
)
def ask_feedback(person_id, person_ids, reason, request_ids):
    who = Person.objects.get(staff_id=person_id)
    suggested_persons = Person.objects.filter(staff_id__in=person_ids).all()

    _requests = _get_feedback_request(who, suggested_persons, reason, request_ids)
    headers = {'Reply-To': who.get_email()}
    messages = []

    for person, message, subject, from_email in _requests:
        message = EmailMessage(
            subject=subject,
            body=message,
            to=[person.get_email()],
            from_email=from_email,
            headers=headers,
        )
        messages.append(message)
    connection = get_connection(fail_silently=False)
    return connection.send_messages(messages)


REQUEST_REMIND_BEFORE_IN_DAYS = 1


@shared_task
@get_lock_or_do_nothing('send_feedback_request_reminders')
def send_feedback_request_reminders(today=None):
    from fb.feedback.models import FeedbackRequest

    if today is None:
        today = date.today()
    elif isinstance(today, basestring):
        today = datetime.strptime(today, '%Y-%m-%d').date()

    deadline_for_today_reminders = today + timedelta(
        days=REQUEST_REMIND_BEFORE_IN_DAYS)

    requests = FeedbackRequest.objects.filter(
        is_submitted=False,
        deadline=deadline_for_today_reminders,
    ).select_related(
        'reporter',
        'suggest_to',
        'suggested_persons',
    )

    messages = []
    for request in requests:
        person, body, subject, from_email = _get_deadline_email_params(request)
        headers = {'Reply-To': request.reporter.get_email()}
        message = EmailMessage(
            subject=subject,
            body=body,
            to=[person.get_email()],
            from_email=from_email,
            headers=headers,
        )
        messages.append(message)

    if messages:
        connection = get_connection(fail_silently=False)
        return connection.send_messages(messages)


def _get_deadline_email_params(feedback_request):
    mail_template = get_template('deadline.html')

    person = feedback_request.suggest_to
    suggested_persons = feedback_request.suggested_persons.all()
    with translation.override(person.language_ui):
        c = Context({
            'mail_template_language': person.language_ui,
            'who': feedback_request.reporter,
            'persons': suggested_persons,
            'base_url': settings.BASE_URL,
            'date': feedback_request.deadline,
            'link': _create_link(suggested_persons, feedback_request.id)
        })
        subject = _('deadline.subject')
        from_email = _format_from_address(
            username=feedback_request.reporter.get_full_name(),
            real_address=settings.EMAIL_HOST_USER,
        )
        body = mail_template.render(c)
    return person, body, subject, from_email


@shared_task
def send_feedback_request_reject(request_id):
    from fb.feedback.models import FeedbackRequest
    feedback_request = FeedbackRequest.objects.get(pk=request_id)
    headers = {'Reply-To': feedback_request.suggest_to.get_email()}

    counter = 0
    for email_data in _get_feedback_request_reject_emails(feedback_request):
        target, body, subject, from_email = email_data
        message = EmailMessage(
            subject=subject,
            body=body,
            to=[target.get_email()],
            from_email=from_email,
            headers=headers,
        )
        message.send()
        counter += 1
    return counter


def _get_feedback_request_reject_emails(feedback_request):
    mail_template = get_template('reject.html')

    suggested_persons_ids = feedback_request.suggested_persons.values_list(
        'staff_id', flat=True)
    one_head_for_each_person = get_heads_for_persons(
        persons_ids=suggested_persons_ids,
        all_heads=False,
    )
    persons_for_heads_map = {}
    for person, head in one_head_for_each_person.items():
        head_persons = persons_for_heads_map.setdefault(head, set())
        head_persons.add(person)

    persons_by_id = {
        person.staff_id: person for person in
        Person.objects.filter(
            staff_id__in=set(one_head_for_each_person) | set(persons_for_heads_map)
        )
    }

    suggest_to = feedback_request.suggest_to
    for head_id, person_ids in persons_for_heads_map.items():
        head = persons_by_id[head_id]
        persons = [persons_by_id[person_id] for person_id in person_ids]
        with translation.override(head.language_ui):
            c = Context({
                'mail_template_language': head.language_ui,
                'target': head,
                'reporter': feedback_request.reporter,
                'who': suggest_to,
                'persons': persons,
                'base_url': settings.BASE_URL,
            })
            subject = ' '.join([
                suggest_to.get_full_name(),
                pgettext('reject.subject', suggest_to.get_gender_display()),
            ])
            from_email = _format_from_address(
                username=suggest_to.get_full_name(),
                real_address=settings.EMAIL_HOST_USER,
            )
            body = mail_template.render(c)
        yield head, body, subject, from_email


# for makemessages command
pgettext('reject.subject', 'male'),
pgettext('reject.subject', 'female'),
