from django.conf import settings
from django.contrib.postgres.search import SearchRank
from django.db.models import Q, F, Value, Count
from django.db.models.functions import Coalesce

from intranet.femida.src.core.db import SearchQuery, SearchQueryEn
from intranet.femida.src.publications.choices import (
    PUBLICATION_LANGUAGES,
    PUBLICATION_TYPES,
    PUBLICATION_STATUSES,
    PUBLICATION_EMPLOYMENT_TYPES,
    PUBLICATION_PRO_LEVELS,
    PUBLICATION_TO_VACANCY_PRO_LEVELS_MAP,
)
from intranet.femida.src.utils.translation import get_language
from intranet.femida.src.vacancies.choices import VACANCY_PRO_LEVELS


def get_publications_lang():
    default_lang = PUBLICATION_LANGUAGES.en
    lang = get_language(default_lang)
    lang = lang if lang in PUBLICATION_LANGUAGES else default_lang
    return lang


def filter_queryset_by_pub_pro_levels(pub_pro_levels, queryset):
    query = Q()
    for level in pub_pro_levels:
        if level == PUBLICATION_PRO_LEVELS.chief:
            query |= Q(is_chief=True)
        else:
            vacancy_pro_levels = PUBLICATION_TO_VACANCY_PRO_LEVELS_MAP.get(level, [])
            for vacancy_pro_level in vacancy_pro_levels:
                query |= (
                    Q(nonnull_pro_level_min__lte=vacancy_pro_level)
                    & Q(vacancy__pro_level_max__gte=vacancy_pro_level)
                )

    return (
        queryset
        .annotate(nonnull_pro_level_min=Coalesce(F('vacancy__pro_level_min'), Value(0)))
        .filter(query)
    )


def filter_queryset_by_pub_employment_types(employment_types, queryset):
    query = Q()
    remote_q = Q(vacancy__cities__id=settings.CITY_HOMEWORKER_ID)
    office_q = Q(cities_count__gt=1) | ~remote_q
    intern_q = Q(vacancy__pro_level_min=VACANCY_PRO_LEVELS.intern) | Q(vacancy__pro_level_min__isnull=True)

    for employment_type in employment_types:
        if employment_type == PUBLICATION_EMPLOYMENT_TYPES.intern:
            query |= intern_q
        elif employment_type == PUBLICATION_EMPLOYMENT_TYPES.remote:
            query |= remote_q
        elif employment_type == PUBLICATION_EMPLOYMENT_TYPES.office:
            query |= office_q
    return (
        queryset
        .annotate(cities_count=Count('vacancy__cities'))
        .filter(query)
    )


def get_suitable_publications(queryset, cities=None, professions=None,
                              public_professions=None, services=None, pro_levels=None,
                              skills=None, text=None, lang=None, employment_types=None):

    if lang is None:
        lang = get_publications_lang()
    queryset = queryset.filter(lang=lang)

    if text:
        sq_class = SearchQuery if lang == PUBLICATION_LANGUAGES.ru else SearchQueryEn
        sq_field = f'search_vector_{lang}'
        search_query = sq_class(text)
        queryset = (
            queryset
            .filter(**{sq_field: search_query})
            .annotate(rank=SearchRank(F(sq_field), search_query))
        )

    if professions:
        queryset = queryset.filter(vacancy__profession__in=professions)

    if public_professions:
        queryset = queryset.filter(vacancy__profession__public_professions__in=public_professions)

    if cities:
        queryset = queryset.filter(vacancy__cities__in=cities)

    if skills:
        queryset = queryset.filter(vacancy__skills__in=skills)

    if services:
        queryset = queryset.filter(public_service__in=services)

    if pro_levels:
        queryset = filter_queryset_by_pub_pro_levels(pro_levels, queryset)

    if employment_types:
        queryset = filter_queryset_by_pub_employment_types(employment_types, queryset)

    return queryset.distinct()


def count_external_publications_annotation(publications_path, lang):
    return Count(
        publications_path,
        filter=Q(**{
            f'{publications_path}__status': PUBLICATION_STATUSES.published,
            f'{publications_path}__type': PUBLICATION_TYPES.external,
            f'{publications_path}__lang': lang,
        })
    )


def get_vacancy_employment_types(vacancy):
    employment_types = []

    cities = vacancy.cities.all().values_list('id', flat=True)
    is_remote = settings.CITY_HOMEWORKER_ID in cities
    is_office = not is_remote or len(cities) > 1

    is_intern = vacancy.pro_level_min in (VACANCY_PRO_LEVELS.intern, None)
    if is_remote:
        employment_types.append(PUBLICATION_EMPLOYMENT_TYPES.remote)
    if is_office:
        employment_types.append(PUBLICATION_EMPLOYMENT_TYPES.office)
    if is_intern:
        employment_types.append(PUBLICATION_EMPLOYMENT_TYPES.intern)

    return employment_types
