from staff.lib.exceptions import ErrorWithStatusCode

from staff.person.models import Staff

from staff.person.models import Contact
from staff.person.controllers import PersonCtl
from staff.person_profile.errors import (
    log_does_not_exist_staff_login,
    log_does_not_exist_staff_id,
)

import logging
logger = logging.getLogger('person_profile.controllers.contacts')


class ContactsError(ErrorWithStatusCode):
    pass


class ContactsCtl(object):
    def __init__(self, login):
        self.login = login
        self._person_id = None
        self._person_contacts = None

    @property
    def person_id(self):
        with log_does_not_exist_staff_login(logger=logger, message_params=[self.login], raise_e=ContactsError):
            self._person_id = self._person_id or (
                Staff.objects.values_list('id', flat=True)
                .get(login=self.login)
            )
        return self._person_id

    def get_person_contacts(self):
        self._person_contacts = self._person_contacts or (
            Contact.objects.values(
                'id',
                'contact_type',
                'account_id',
                'private',
            )
            .filter(person__login=self.login)
            .order_by('position')
        )
        return self._person_contacts

    def update_contacts(self, new_contacts_data):
        old_ids = set(
            Contact.objects.values_list('id', flat=True)
            .filter(person_id=self.person_id)
        )

        for position, form_data in enumerate(new_contacts_data):
            pk = form_data.get('id')
            contact_data = {
                'person_id': self.person_id,
                'contact_type': form_data['contact_type'],
                'account_id': form_data['account_id'],
                'position': position,
                'private': form_data['private'],
            }

            if pk is not None:
                contact_data['id'] = pk
                old_ids.discard(pk)
            try:
                Contact(**contact_data).save(
                    force_insert=not bool(pk),
                    force_update=bool(pk)
                )
            except Exception as e:
                logger.exception('Error while trying to save Contact')
                raise ContactsError(str(e))

        if old_ids:
            for contact in Contact.objects.filter(id__in=old_ids):
                try:
                    contact.delete()
                except Contact.DoesNotExist as e:
                    logger.exception('Error while trying to delete Contact')
                    raise ContactsError(str(e))

        try:
            _sync_contacts(self.person_id)
        except Exception as e:
            logger.exception('Error while trying to sync Contacts')
            raise ContactsError(str(e))


# ID хардкодом в fixtures: base_contact_types.json
SYNC_IDS = {
    1: 'home_email',
    2: 'jabber',
    3: 'icq',
    4: 'login_skype',
    5: 'login_twitter',
    6: 'login_mk',
    7: 'home_page',
}


def _sync_contacts(person_id):
    with log_does_not_exist_staff_id(logger=logger, message_params=[person_id], raise_e=ContactsError):
        staff = Staff.objects.get(id=person_id)

    new_contacts = (
        Contact.objects.filter(
            person_id=person_id,
            contact_type_id__in=SYNC_IDS.keys(),
        ).order_by('id')
    )
    skip_types = set()
    data = {}
    for new_contact in new_contacts:
        type_id = new_contact.contact_type_id
        if type_id not in skip_types:
            data[SYNC_IDS[type_id]] = new_contact.account_id
            skip_types.add(type_id)

    for type_id, field in SYNC_IDS.items():
        if type_id not in skip_types:
            data[SYNC_IDS[type_id]] = ''

    PersonCtl(staff).update(data).save(staff.user)
