from typing import Dict

from django.db.models import OuterRef, Subquery, Value, F
from django.db.models.functions import Coalesce

from intranet.femida.src.candidates.choices import (
    CONSIDERATION_STATUSES,
    CONSIDERATION_EXTENDED_STATUSES,
)
from intranet.femida.src.candidates.models import Candidate, Consideration
from intranet.femida.src.core.models import City
from intranet.femida.src.skills.models import Skill
from intranet.femida.src.core.db import RowToDict, RowsToList as Rows
from intranet.femida.src.interviews.helpers import InterviewVisibilityHelper
from intranet.femida.src.interviews.models import Interview
from intranet.femida.src.utils.common import ObjectDict


Row = lambda x: RowToDict(x[:1])


_CANDIDATE_EXTENDED_STATUS_SUBQUERY = (
    Consideration.unsafe
    .filter(
        candidate_id=OuterRef('id'),
        state=CONSIDERATION_STATUSES.in_progress,
    )
    .values('extended_status')
)

_CANDIDATE_TARGET_CITIES_SUBQUERY = (
    City.objects
    .filter(candidates=OuterRef('id'))
    .values('id', 'name_ru', 'name_en')
)

_CANDIDATE_SKILLS_SUBQUERY = (
    Skill.objects
    .filter(candidates=OuterRef('id'))
    .values('id', 'name')
)

_APPLICATION_CANDIDATE_SUBQUERY = (
    Candidate.unsafe
    .annotate(extended_status=Coalesce(
        Subquery(_CANDIDATE_EXTENDED_STATUS_SUBQUERY),
        Value(CONSIDERATION_EXTENDED_STATUSES.archived),
    ))
    .filter(id=OuterRef('candidate_id'))
    .values(
        'id',
        'first_name',
        'last_name',
        'extended_status',
        target_cities_=Rows(_CANDIDATE_TARGET_CITIES_SUBQUERY),
        skills_=Rows(_CANDIDATE_SKILLS_SUBQUERY),
    )
)

_CONSIDERATION_INTERVIEWS_SUBQUERY = (
    Interview.unsafe
    .filter(consideration_id=OuterRef('id'))
    .alive()
    .values(
        'id',
        'type',
        'grade',
        'yandex_grade',
        'state',
        'resolution',
        'interviewer_id',
        'application_id',
        vacancy_id=F('application__vacancy_id'),
    )
)

_APPLICATION_CONSIDERATION_SUBQUERY = (
    Consideration.unsafe
    .filter(id=OuterRef('consideration_id'))
    .values(
        'id',
        'extended_status',
        'state',
        'resolution',
        interviews_=Rows(_CONSIDERATION_INTERVIEWS_SUBQUERY),
    )
)


class ApplicationDashboardListSerializer:
    """
    Сериализатор прет-в для Дашборда, мимикрирующий под списковый DRF-сериализатор
    """
    def __init__(self, instance, *args, **kwargs):
        self.user = kwargs['context']['user']
        self.data = [self._clean(i) for i in instance]

    def _clean(self, item):
        item['candidate'] = item.pop('candidate_')
        item['candidate']['skills'] = item['candidate'].pop('skills_', []) or []
        item['candidate']['target_cities'] = self._clean_target_cities(item['candidate'])

        item['consideration'] = item.pop('consideration_')
        item['consideration']['interviews'] = self._clean_interviews(item['consideration'])
        return item

    def _clean_interviews(self, consideration: Dict):
        result = []
        interviews = consideration.pop('interviews_', []) or []
        cons_obj = ObjectDict(consideration or {})
        interviews = [ObjectDict(i) for i in interviews]
        helper = InterviewVisibilityHelper(self.user, cons_obj, interviews)
        for interview in interviews:
            if not helper.is_visible(interview):
                interview.pop('grade')
                interview.pop('yandex_grade')
                interview.pop('resolution')
            result.append(interview)
        return result

    @staticmethod
    def _clean_target_cities(candidate: Dict):
        result = []
        target_cities = candidate.pop('target_cities_', []) or []
        for city in target_cities:
            # При инициализации задействуется I18NNameModelMixin, который проставляет поле name
            city_instance = City(name_ru=city.pop('name_ru'), name_en=city.pop('name_en'))
            city['name'] = city_instance.name
            result.append(city)
        return result

    @staticmethod
    def setup_eager_loading(queryset, *args, **kwargs):
        return queryset.values(
            'id',
            'comments_count',
            candidate_=Row(_APPLICATION_CANDIDATE_SUBQUERY),
            consideration_=Row(_APPLICATION_CONSIDERATION_SUBQUERY),
        )
