import base64
import re

from staff.person.models.contacts import ContactTypeId

from django.conf import settings
from django.db.models.query import QuerySet
from django.template import loader, Context, Template

from staff.lib import requests
from staff.person.models import Contact, Staff, StaffPhone, PHONE_KIND, PHONE_TYPES, PHONE_PROTOCOLS


VCARD_TEMPLATE = 'vcard.template.html'


def _get_raw_photo(login):
    photo_url = '%s://%s/api/v1/user/%s/photo/300.jpg' % (
        settings.CENTER_PROTOCOL,
        settings.CENTER_MASTER,
        login,
    )
    photo_content = requests.get(url=photo_url, timeout=3).content
    return base64.b64encode(photo_content).replace(b'\n', b'\n ').strip()


def _prepare_phones(phones):
    def get_types(phone):
        result = []
        types = {
            PHONE_TYPES.MOBILE: 'cell',
            PHONE_TYPES.HOME: 'home',
        }
        protocols = {
            PHONE_PROTOCOLS.ALL: ['voice', 'msg'],
            PHONE_PROTOCOLS.SMS: ['msg'],
            PHONE_PROTOCOLS.VOICE: ['voice'],
        }
        result.append(types.get(phone.type))
        result.extend(protocols.get(phone.protocol))
        return result or ['cell']

    prepared = [
        {'number': p.number, 'types': ', '.join(get_types(p))}
        for p in phones
    ]
    if prepared:
        prepared[0]['types'] += ', pref'
    return prepared


def _prepare_contacts(person: Staff, include_contacts: bool):
    contacts_qs = (
        Contact.objects
        .filter(
            person_id=person.id,
            contact_type__show_in_vcard=True,
        )
        .exclude(contact_type__vcard_template="")
        .order_by('position')
        .values('account_id', 'contact_type__vcard_template')
    )

    if not include_contacts:
        public_contacts = [
            ContactTypeId.telegram.value,
            ContactTypeId.assistant.value,
            ContactTypeId.another_work_email.value,
        ]
        contacts_qs = contacts_qs.filter(contact_type_id__in=public_contacts)

    return [
        Template(c['contact_type__vcard_template'])
        .render(context=Context({'account_id': c['account_id']}))
        for c in contacts_qs
    ]


def _prepare_person(person):
    return{
        'first_name': person.i_first_name,
        'last_name': person.i_last_name,
        'email': person.get_email(),
        'position': person.i_position,
        'department_name': person.department.name,
        'birthday': person.birthday,
        'home_email': person.home_email,
        'work_phone': person.work_phone,
        'login_passport': person.login_passport,
        'is_login_passport_confirmed': person.is_login_passport_confirmed,
        'tz': person.tz,
    }


class VCardGenerator:
    n_re = re.compile(r'\n{2,}')
    template = loader.get_template(VCARD_TEMPLATE)

    def _generate(self, person: Staff, include_contacts: bool, photo=None, extend=True):
        phones_qs = (
            StaffPhone.objects
            .filter(intranet_status=1, staff_id=person.id)
            .exclude(kind__in=[PHONE_KIND.EMERGENCY, PHONE_KIND.HIDDEN])
        )

        if not include_contacts:
            phones_qs = phones_qs.exclude(type=PHONE_TYPES.MOBILE)

        context = Context({
            'person': _prepare_person(person),
            'phones': _prepare_phones(phones_qs.order_by('position')),
            'extra_contacts': _prepare_contacts(person, include_contacts),
            'photo': photo,
            'extend': extend,
        })
        resp = self.template.render(context).strip()
        resp = self.n_re.sub('\n', resp)
        return resp + '\n\n'

    def __call__(self, person, include_contacts: bool, ignore_photo=False, extend=True, force_update=False):
        photo = None if ignore_photo else _get_raw_photo(person.login)
        return self._generate(person, include_contacts, photo=photo, extend=extend)


vcard_generator = VCardGenerator()


def _get_vcard(person, include_contacts: bool, **kwargs):
    """
    Возвращает карточку vcard сотрудника.
    Если ignore_photo=True или extend=False, то генерит vcard налету.
    В противном случае берет картинку из кэша.

    Параметры:
    person - объект Staff. Обязательный параметр.
    ignore_photo - отдать vcard без фотографии. По-умолчанию False.
    extend - выдать "сокращенный" vcard. По-умолчанию True
    force_update - перегенерить и обновить в кэше принудительно
    """
    return vcard_generator(person, include_contacts, **kwargs)


def _get_vcards(persons, include_contacts: bool, **kwargs):

    if isinstance(persons, QuerySet):
        persons = persons.select_related('department')

    for person in persons:
        yield vcard_generator(person, include_contacts, **kwargs)
