import logging
import yenv

from staff.lib.utils.staff7344 import is_volozh_kostyl, delegate_volozh_for_gap
from staff.person_filter.saved_filter_ctl import get_subscribers

from staff.gap.controllers.templates import TemplatesCtl, GapTemplateContext, get_templates_by_logins
from staff.gap.controllers.utils import recognize_emails


logger = logging.getLogger(__name__)


class EmailCtl(object):

    def __init__(
        self,
        data: dict,
        approver: dict = None,
        send_not_self=True,
        include_subscribers=True,
        context_template_cls=GapTemplateContext,
    ):
        """
        :param data: dict with data of gap or periodic_gap
        :param approver: dict with id, login and work_email fields from Staff model
        """
        self.data = data
        self.approver = approver
        self.send_not_self = send_not_self
        self.include_subscribers = include_subscribers
        self.context_template_cls = context_template_cls

    def _append_to_send(self, to_send, email, t_data, tag):
        if t_data['template']:
            to_send.append({
                'email': email,
                'template': t_data['template'],
                'context': t_data['context'],
                'tag': tag,
            })

    def _append_login_to_send(self, to_send, login, email, workflow, tag):
        templates = get_templates_by_logins(
            [login], 'email', workflow, tag)
        if templates:
            self._append_to_send(to_send, email, templates[login], tag)

    def _merge_subscribers(self, person_id, staff_to_notify_pairs, exclude_from_subscribers):
        try:
            subscriber_pairs = {
                (s['login'], s['work_email'])
                for s in get_subscribers(person_id, 'absences').values('login', 'work_email')
                if s['login'] not in exclude_from_subscribers
            }
        except AssertionError:
            logger.exception('_merge_subscribers')
            subscriber_pairs = set()
        return staff_to_notify_pairs | subscriber_pairs

    def _base_tag_to_send(self, person, modifier, workflow, tag, to_notify_overwrite):
        data_to_notify = to_notify_overwrite or self.data['to_notify']
        to_notify = recognize_emails(data_to_notify)

        staff_to_send = self._staff_to_send(to_notify['staff'], person, modifier, workflow, tag)
        ml_to_send = self._simple_to_send(to_notify['ml'], person, workflow, tag)
        unknown_to_send = self._simple_to_send(to_notify['unknown'], person, workflow, tag + '_unknown')

        return self._join_to_cc(staff_to_send) + ml_to_send + unknown_to_send

    def _staff_to_send(self, staff_to_notify, person, modifier, workflow, tag):
        person_id = person['id']
        person_login = person['login']

        staff_to_send = []
        exclude_from_subscribers = []

        if self.approver:
            # письмо [email / tag_chief] утверждающему
            if is_volozh_kostyl(self.approver['login'], person_login):
                self.approver = delegate_volozh_for_gap()
            self._append_login_to_send(staff_to_send, self.approver['login'], self.approver['work_email'],
                                       workflow, tag + '_chief')
            exclude_from_subscribers.append(self.approver['login'])

        if self.include_subscribers:
            staff_pairs = self._merge_subscribers(person_id, staff_to_notify, exclude_from_subscribers)
        else:
            staff_pairs = staff_to_notify

        staff_templates = get_templates_by_logins(
            [p[0] for p in staff_pairs],
            'email',
            workflow,
            tag,
        )

        # письмо [email / tag] уведомляемым и подписчикам
        for login, email in staff_pairs:
            self._append_to_send(staff_to_send, email, staff_templates.get(login), tag)

        # письмо [email / tag_not_self] тому, чьё отсутствие
        if self.send_not_self and person_id != modifier['id']:
            self._append_login_to_send(staff_to_send, person_login, person['work_email'], workflow, tag + '_not_self')

        return staff_to_send

    def _simple_to_send(self, to_notify, person, workflow, tag):
        if not to_notify:
            return []

        template = TemplatesCtl().find_one_not_strict(
            type='email',
            workflow=workflow,
            tag=tag,
            lang=person['lang_ui'] or 'ru',
        )

        if not template:
            return []

        return [{
            'template': template,
            'to_send': [
                {'email': email, 'context': {}} for email in to_notify
            ],
            'tag': tag,
        }]

    def notify(self, person, modifier, workflow, tag, gap_diff=None, to_notify_overwrite=None):
        to_send = self._base_tag_to_send(person, modifier, workflow, tag, to_notify_overwrite)
        if to_send:
            self._send(person, modifier, to_send, gap_diff)

    def _send(self, person, modifier, to_send, data_diff):
        from staff.gap.tasks import SendMail

        context = self.context_template_cls(
            person=person,
            modifier=modifier,
            data=self.data,
            data_diff=data_diff,
        ).context()

        for data in to_send:
            if '_id' in data['template']:
                del data['template']['_id']

        task = SendMail
        if yenv.type != 'development':
            task = task.delay

        task(gap_id=self.data['id'], to_send=to_send, context=context)

    def _prepend_to_keys(self, prefix, data):
        return {'%s%s' % (prefix, key): value for key, value in data.items()}

    def _join_to_cc(self, to_send):
        result = {}
        for data in to_send:
            t_id = data['template']['_id']
            to_send = result.setdefault(t_id, {
                'template': data['template'],
                'to_send': [],
            })
            to_send['to_send'].append({
                'email': data['email'],
                'context': data['context'],
                'tag': data['tag'],
            })
        return list(result.values())
