from collections import defaultdict

from django.db.models import Case, When, IntegerField
from django.utils import timezone

from intranet.femida.src.candidates.choices import CONSIDERATION_STATUSES
from intranet.femida.src.candidates.models import (
    Consideration,
    ConsiderationResponsible,
    Challenge,
)
from intranet.femida.src.candidates.signals import consideration_status_changed
from intranet.femida.src.communications.choices import COMMENT_MESSAGE_TYPES
from intranet.femida.src.communications.models import Message
from intranet.femida.src.core.controllers import update_instance
from intranet.femida.src.core.db.helpers import get_count_subquery
from intranet.femida.src.core.signals import post_bulk_create
from intranet.femida.src.interviews.models import Application, Interview
from intranet.femida.src.permissions.helpers import get_manager


CONSIDERATION_STATUSES_ORDER = {
    CONSIDERATION_STATUSES.in_progress: 1,
    CONSIDERATION_STATUSES.archived: 2,
}


def get_sorted_considerations(qs=None, unsafe=False):
    qs = get_manager(Consideration, unsafe) if qs is None else qs
    return (
        qs
        .annotate(
            state_order=Case(
                default=100,
                output_field=IntegerField(),
                *[When(state=s, then=o) for s, o in CONSIDERATION_STATUSES_ORDER.items()]
            ),
        )
        .order_by('state_order', '-started')
    )


def add_items_counts_to_considerations_qs(qs):
    challenges_count_q = get_count_subquery(
        queryset=Challenge.objects.alive(),
        reverse_related_name='consideration_id',
    )
    interviews_count_q = get_count_subquery(
        queryset=Interview.unsafe.alive(),
        reverse_related_name='consideration_id',
    )
    applications_count_q = get_count_subquery(
        queryset=Application.unsafe.all(),
        reverse_related_name='consideration_id',
    )
    messages_count_q = get_count_subquery(
        queryset=(
            Message.unsafe
            .alive()
            .filter(type__in=COMMENT_MESSAGE_TYPES._db_values)
        ),
        reverse_related_name='application__consideration_id',
    )
    return (
        qs
        .annotate(
            assessments_count=interviews_count_q + challenges_count_q,
            applications_count=applications_count_q,
            messages_count=messages_count_q,
        )
    )


def _get_considerations_items_counts(queryset):
    """
    Возвращает разные каунтеры по активным/архивным рассмотрениям кандидата
    для испытаний и претендентств
    """
    queryset = (
        add_items_counts_to_considerations_qs(queryset)
        .values(
            'id',
            'assessments_count',
            'applications_count',
            'messages_count',
        )
    )

    res = defaultdict(dict)
    for cons in queryset:
        cons_id = cons.pop('id')
        for key in cons:
            res[cons_id][key.replace('_count', '')] = cons[key]

    return res


def get_considerations_items_counts(consideration_ids):
    qs = Consideration.unsafe.filter(id__in=consideration_ids)
    return _get_considerations_items_counts(qs)


def get_consideration_items_counts(consideration_id):
    return get_considerations_items_counts([consideration_id])[consideration_id]


def archive_consideration(consideration, resolution):
    """
    Закрывает рассмотрение и переносит необходимые поля из кандидата в рассмотрение
    """
    candidate = consideration.candidate
    update_instance(consideration, data={
        'state': CONSIDERATION_STATUSES.archived,
        'extended_status': CONSIDERATION_STATUSES.archived,
        'source': candidate.source,
        'source_description': candidate.source_description,
        'resolution': resolution,
        'finished': timezone.now(),
    })
    consideration_status_changed.send(sender=Consideration, consideration=consideration)

    candidate_responsibles = ConsiderationResponsible.objects.bulk_create([
        ConsiderationResponsible(
            consideration=consideration,
            user_id=candidate_responsible.user_id,
            role=candidate_responsible.role,
        )
        for candidate_responsible in candidate.candidate_responsibles.all()
    ])
    post_bulk_create.send(
        sender=ConsiderationResponsible,
        queryset=candidate_responsibles,
    )


def get_suitable_in_progress_considerations(queryset, stage, users, role=None, professions=None,
                                            only_with_issues=False, issue_types=(), vacancies=None,
                                            **kwargs):
    queryset = queryset.filter(
        state=CONSIDERATION_STATUSES.in_progress,
        extended_status=stage,
    )
    if role:
        queryset = queryset.filter(
            candidate__responsibles__in=users,
            candidate__candidate_responsibles__role=role,
        )
    else:
        queryset = queryset.filter(
            candidate__responsibles__in=users,
        )
    if professions:
        queryset = queryset.filter(candidate__candidate_professions__profession__in=professions)

    if only_with_issues:
        filter_params = {'consideration_issues__is_resolved': False}
        if issue_types:
            filter_params['consideration_issues__type__in'] = issue_types
        queryset = queryset.filter(**filter_params)

    if vacancies:
        queryset = queryset.filter(applications__vacancy__in=vacancies)

    return queryset.distinct()


def get_suitable_archived_considerations(queryset, stage, users, role=None, professions=None,
                                         finished__gte=None, finished__lte=None, **kwargs):
    queryset = queryset.filter(
        state=CONSIDERATION_STATUSES.archived,
        resolution=stage,
    )
    if role:
        queryset = queryset.filter(
            consideration_responsibles__user__in=users,
            consideration_responsibles__role=role,
        )
    else:
        queryset = queryset.filter(
            responsibles__in=users,
        )
    if professions:
        queryset = queryset.filter(candidate__candidate_professions__profession__in=professions)
    if finished__gte:
        queryset = queryset.filter(finished__gte=finished__gte)
    if finished__lte:
        queryset = queryset.filter(finished__lte=finished__lte)
    return queryset.distinct()
