from django.utils.functional import cached_property

from intranet.femida.src.attachments.models import Attachment
from intranet.femida.src.candidates.contacts import normalize_contact
from intranet.femida.src.candidates.submissions.helpers import extract_contacts
from intranet.femida.src.candidates.models import Candidate, CandidateSubmission
from intranet.femida.src.utils.strings import unaccent


def _normalize_text(value):
    return unaccent(value.strip().lower())


def to_cand_adapter(obj):
    if isinstance(obj, CandidateAdapter):
        return obj
    elif isinstance(obj, dict):
        return CandidateDictAdapter(obj)
    elif isinstance(obj, Candidate):
        return CandidateInstanceAdapter(obj)
    elif isinstance(obj, CandidateSubmission):
        return CandidateSubmissionAdapter(obj)
    else:
        raise TypeError(
            'Invalid object provided. Object should be the instance of either '
            'CandidateAdapter, dict, Candidate or CandidateSubmission'
        )


class CandidateAdapter:
    """
    Базовый адаптер для инстансов Candidate и словарей из валидаторов.
    Нужен, чтобы предоставить единый интерфейс для поиска дубликатов.
    """


class CandidateInstanceAdapter(CandidateAdapter):

    def __init__(self, candidate):
        self.candidate = candidate

    def __getattr__(self, name):
        if name in ('id', 'login', 'birthday', 'gender', 'source', 'inn'):
            return getattr(self.candidate, name)
        if name in ('first_name', 'middle_name', 'last_name',
                    'country', 'city', 'source_description'):
            return _normalize_text(getattr(self.candidate, name))
        raise AttributeError

    @cached_property
    def contacts(self):
        return {
            (c.type, c.normalized_account_id)
            for c in self.candidate.contacts.all()
            if c.normalized_account_id and c.is_active
        }

    @cached_property
    def attachment_hashes(self):
        return {
            ca.attachment.sha1
            for ca in self.candidate.candidate_attachments.all()
        }


class CandidateDictAdapter(CandidateAdapter):

    def __init__(self, data):
        self.data = data

    def __getattr__(self, name):
        if name in ('id', 'login', 'birthday', 'gender', 'source', 'inn'):
            return self.data.get(name)
        if name in ('first_name', 'middle_name', 'last_name',
                    'country', 'city', 'source_description'):
            return _normalize_text(self.data.get(name, ''))
        raise AttributeError

    @cached_property
    def contacts(self):
        return {
            (c['type'], normalize_contact(c['type'], c['account_id']))
            for c in self.data.get('contacts', [])
            if c['account_id']
        }

    @cached_property
    def attachment_hashes(self):
        attachment_ids = [a.id for a in self.data.get('attachments', [])]
        if attachment_ids:
            return set(
                Attachment.objects.filter(id__in=attachment_ids).values_list('sha1', flat=True)
            )
        return set()


class CandidateSubmissionAdapter(CandidateAdapter):

    def __init__(self, submission):
        data = {
            'first_name': submission.first_name,
            'last_name': submission.last_name,
            'contacts': extract_contacts(submission),
            'attachments': [submission.attachment] if submission.attachment else [],
        }
        if submission.rotation:
            data['login'] = submission.rotation.created_by.username
        if submission.candidate:
            # Для того, чтобы среди дублей отклика не показывался кандидат,
            # созданный этим же откликом
            data['id'] = submission.candidate.id
        self.from_dict_adapter = CandidateDictAdapter(data)

    def __getattr__(self, name):
        return getattr(self.from_dict_adapter, name)
