from django.conf import settings
from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Count, F

from intranet.femida.src.api.cities import serializers
from intranet.femida.src.api.core.pagination import JobsPagination
from intranet.femida.src.api.core.permissions import StrictServicePermission
from intranet.femida.src.api.core.views import BaseView
from intranet.femida.src.core.models import City
from intranet.femida.src.publications.choices import PUBLICATION_STATUSES, PUBLICATION_TYPES
from intranet.femida.src.publications.helpers import get_publications_lang
from intranet.femida.src.publications.models import Publication
from intranet.femida.src.services.models import PublicService
from intranet.femida.src.utils.cache import memoize


def _get_cities_with_external_publications_qs(lang):
    return (
        City.objects
        .filter(
            is_active=True,
            vacancies__publications__status=PUBLICATION_STATUSES.published,
            vacancies__publications__type=PUBLICATION_TYPES.external,
            vacancies__publications__lang=lang,
        )
        .exclude(id=settings.CITY_HOMEWORKER_ID)
    )


@memoize(seconds=60 * 60, cache_prefix=settings.JOBS_CACHE_PREFIX)
def get_city_service_mapping(lang):
    return dict(
        _get_cities_with_external_publications_qs(lang)
        .annotate(
            services=ArrayAgg(
                'vacancies__publications__public_service',
                distinct=True,
            )
        )
        .values_list('id', 'services')
    )


@memoize(seconds=60 * 60, cache_prefix=settings.JOBS_CACHE_PREFIX)
def get_service_external_publications_count_by_city(lang) -> dict[tuple, int]:
    queryset = (
        _get_cities_with_external_publications_qs(lang)
        .annotate(
            publications_count=Count('vacancies__publications'),
            public_service_id=F('vacancies__publications__public_service'),
        )
        .values('id', 'public_service_id', 'publications_count')
    )
    return {
        (row['id'], row['public_service_id']): row['publications_count']
        for row in queryset
    }


class CityBaseView(BaseView):

    model_class = City
    authentication_classes = []
    permission_classes = [
        StrictServicePermission('permissions.can_view_external_publications'),
    ]


class CityListView(CityBaseView):

    pagination_class = JobsPagination
    list_item_serializer_class = serializers.CitySerializer

    def get_queryset(self):
        return self.model_class.objects.filter(is_active=True).exclude(id=settings.CITY_HOMEWORKER_ID)

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def get_list_item_serializer_context(self):
        lang = get_publications_lang()
        city_ids = [c.id for c in self.page]
        publications_count_by_city_id = dict(
            Publication.objects
            .published_external()
            .filter(
                lang=lang,
                vacancy__cities__in=city_ids,
            )
            .values('vacancy__cities')
            .annotate(publications_count=Count('id'))
            .values_list('vacancy__cities', 'publications_count')
        )
        service_ids_by_city_id = get_city_service_mapping(lang)
        return {
            'service_publications_count_by_city': get_service_external_publications_count_by_city(lang),
            'public_service_by_id': PublicService.objects.in_bulk(),
            'service_ids_by_city_id': service_ids_by_city_id,
            'publications_count_by_city_id': publications_count_by_city_id,
        }


class CityDetailView(CityBaseView):

    detail_serializer_class = serializers.CitySerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
