from collections import defaultdict
from datetime import date, datetime
from typing import Any, Dict

from django.conf import settings

from staff.lib.utils.qs_values import localize, extract_related

from staff.innerhire.models import Candidate
from staff.gap.controllers.staff_utils import GapsByPersons
from staff.person_filter.filter_context import FilterContext
from staff.person.models import Staff
from staff.person.models.contacts import ContactTypeId


class PersonsListFiller:
    def __init__(self, filter_context: FilterContext):
        self.observer_tz = filter_context.observer_tz
        self.context = filter_context

    def _short_clean(self, person):
        person = localize(person)
        return person

    def _clean(self, person):
        person = self._short_clean(person)
        person['office'] = extract_related(person, 'office')

        today = date.today()
        birthday = person.pop('birthday')
        person['is_birthday'] = (
            birthday
            and
            birthday.day == today.day
            and
            birthday.month == today.month
        )

        # признак новичка
        join_at = person['join_at']
        person['is_beginner'] = (today - join_at).days < settings.BEGINNER_DAYS

        # последнюю активность
        last_activity = extract_related(person, 'stafflastoffice')
        if last_activity['updated_at']:
            last_activity['seconds_ago'] = int((
                datetime.now() - last_activity.pop('updated_at')
            ).total_seconds())
            person['last_activity'] = last_activity

        return person

    def _get_telegram(self, all_ids):
        q = (
            Staff.objects
            .filter(id__in=all_ids, contact__contact_type_id=ContactTypeId.telegram.value)
            .values('id', 'contact__account_id')
        )

        res = defaultdict(list)

        for tg in q:
            res[tg['id']].append(tg['contact__account_id'])

        return res

    def _get_gaps(self, persons: Dict[int, Dict[str, Any]]):
        gaps = (
            GapsByPersons(datetime.now(), observer_tz=self.observer_tz)
            .sort_by_rank()
            .only_one()
            .get_by_ids(list(persons.keys()))
        )

        tigran_is_viewer = self.context.user.get_profile().login == settings.LOGIN_TIGRAN

        return {
            person_id: [self._prepare_one_gap(gap)]
            for person_id, gap in gaps.items()
            if persons[person_id]['login'] != settings.LOGIN_TIGRAN or tigran_is_viewer
        }

    def _prepare_one_gap(self, gap):
        gap_data = {
            'name': gap['workflow'],
            'left_edge': gap['left_edge'],
            'right_edge': gap['right_edge'],
            'date_from': gap['date_from'],
            'date_to': gap['date_to'],
            'full_day': gap['full_day'],
            'work_in_absence': gap['work_in_absence'],
            'description': gap['comment'],
            'id': gap['id'],
        }

        if gap['workflow'] == 'illness':
            gap_data['is_covid'] = gap.get('is_covid', False)
        elif gap['workflow'] == 'duty':
            for field in ('service_slug', 'service_name', 'shift_id', 'role_on_duty'):
                gap_data[field] = gap.get(field)

        return gap_data

    def _get_candidates(self, all_ids):
        """Ищущие работу"""
        return set(
            Candidate.objects
            .filter(person_id__in=all_ids, date_close=None)
            .values_list('person_id', flat=True)
        )

    def _fill(self, persons):
        persons_by_ids = {person['id']: person for person in persons}

        candidates_set = self._get_candidates(persons_by_ids.keys())
        gaps_map = self._get_gaps(persons_by_ids)
        telegram_map = self._get_telegram(persons_by_ids.keys())

        for person in persons:
            person_id = person['id']

            if person_id in candidates_set:
                person['is_candidate'] = True

            if person_id in gaps_map:
                person['gaps'] = gaps_map[person_id]

            if person_id in telegram_map:
                person['telegram'] = telegram_map[person_id]

            yield person

    def get_as_list(self, persons):
        return [self._clean(person) for person in self._fill(persons)]

    def get_chiefs_as_list(self, persons):
        return [self._short_clean(person) for person in persons]
