"""Endpoints de cobertura."""

from django.http import StreamingHttpResponse
from drf_spectacular.utils import extend_schema
from rest_framework import serializers, status
from rest_framework.response import Response

from apps.core.api import BaseApiView
from apps.core.errors import NotFound, ValidationFailed
from apps.coverage.services import (
    EXPORT_THRESHOLD,
    coverage_summary,
    cycle_estimate,
    export_csv,
    filtered_urls,
    queue_export,
    queue_inspection,
    url_history,
    urls_for_display,
)
from apps.domains.api import company_domain_or_error
from apps.jobs.api import BatchSerializer
from apps.jobs.models import BatchOrigin
from apps.sitemaps.models import Url


class UrlSerializer(serializers.ModelSerializer):
    class Meta:
        model = Url
        fields = [
            'id',
            'loc',
            'in_sitemap',
            'coverage_state',
            'last_checked_at',
            'first_seen_at',
            'last_seen_in_sitemap_at',
        ]


class CoveragePageSerializer(serializers.Serializer):
    items = UrlSerializer(many=True)
    count = serializers.IntegerField()
    page = serializers.IntegerField()
    per_page = serializers.IntegerField()
    pages = serializers.IntegerField()


class CoverageSummarySerializer(serializers.Serializer):
    total = serializers.IntegerField()
    with_data = serializers.IntegerField()
    without_data = serializers.IntegerField()
    indexed = serializers.IntegerField()
    indexed_percentage = serializers.FloatField(allow_null=True)
    by_state = serializers.DictField(child=serializers.IntegerField())
    last_checked_at = serializers.DateTimeField(allow_null=True)


class CoverageListView(BaseApiView):
    @extend_schema(responses=CoveragePageSerializer, tags=['coverage'])
    def get(self, request, domain_id):
        domain = company_domain_or_error(domain_id)
        page = urls_for_display(
            domain,
            state=request.query_params.get('state', ''),
            page=int(request.query_params.get('page', 1) or 1),
        )
        return Response(CoveragePageSerializer(page).data)


class CoverageSummaryView(BaseApiView):
    @extend_schema(responses=CoverageSummarySerializer, tags=['coverage'])
    def get(self, request, domain_id):
        domain = company_domain_or_error(domain_id)
        data = coverage_summary(domain) | {'cycle': cycle_estimate(domain)}
        return Response(data)


class CoverageExportView(BaseApiView):
    @extend_schema(responses={200: None, 202: BatchSerializer}, tags=['coverage'])
    def get(self, request, domain_id):
        """
        Exporta la cobertura como CSV, con la fecha de obtención en su columna.

        La fecha va como columna y no en una nota al pie porque el archivo se
        abre en otro lado, meses después, y un estado sin la fecha en que Google
        lo dijo deja de significar algo.

        El archivo lleva **las mismas columnas que la tabla de la pantalla**. Una
        columna que se ve y no se exporta convierte al CSV en un resumen de otra
        cosa: quien abre la planilla para revisar por qué una URL no está
        indexada necesita el motivo que informó Google, no sólo nuestra
        traducción del motivo (V7, R-A).

        Se lleva **los mismos filtros** que la pantalla, leídos de la misma
        declaración que usa la tabla: un archivo con un recorte distinto del que
        se estaba mirando es peor que no exportar nada, porque nadie lo vuelve a
        revisar (FR-058).

        Dos comportamientos según el volumen. Por debajo del umbral el archivo
        sale en esta misma respuesta. Por encima **no puede**: un sitio de
        128.000 URLs no entra en una respuesta HTTP, y transmitirlo igual deja a
        quien lo pidió mirando una descarga que no arranca mientras el proceso
        que la sirve queda tomado. En ese caso se encola un lote y se contesta
        `202`, que es lo que de verdad pasó.
        """
        domain = company_domain_or_error(domain_id)

        if filtered_urls(domain, request.query_params).count() > EXPORT_THRESHOLD:
            batch = queue_export(domain, request.query_params, origin=BatchOrigin.API)
            return Response(BatchSerializer(batch).data, status=status.HTTP_202_ACCEPTED)

        response = StreamingHttpResponse(
            export_csv(domain, request.query_params), content_type='text/csv; charset=utf-8'
        )
        response['Content-Disposition'] = f'attachment; filename="coverage-{domain.hostname}.csv"'
        return response


class CoverageHistoryView(BaseApiView):
    @extend_schema(responses={200: None}, tags=['coverage'])
    def get(self, request, domain_id):
        """
        Línea de tiempo de una URL: cada cambio de estado con su fecha (FR-060).

        Es la única forma de verificar el caso que dispara una alerta —una URL
        que estaba indexada y dejó de estarlo—, y por eso cada entrada viaja con
        su fecha de obtención y con el lote que la produjo.
        """
        domain = company_domain_or_error(domain_id)
        loc = (request.query_params.get('url') or '').strip()

        if not loc:
            raise ValidationFailed('Indicá la dirección de la URL en el parámetro «url».')

        data = url_history(domain, loc)
        if data is None:
            raise NotFound('Esa dirección no figura entre las URLs de este dominio.')

        return Response(data)


class DomainInspectionsView(BaseApiView):
    @extend_schema(request=None, responses={202: BatchSerializer}, tags=['coverage'])
    def post(self, request, domain_id):
        """
        Encola una tanda de inspecciones, contra la reserva manual del dominio.

        Y la encola de verdad: antes recorría las URLs dentro de la petición
        —hasta dos mil consultas a Google, una detrás de otra— y contestaba
        `202` igual, o sea que la respuesta afirmaba haber encolado algo que en
        realidad se estaba ejecutando ahí mismo.
        """
        replay = self.replay(request)
        if replay is not None:
            return replay

        domain = company_domain_or_error(domain_id)
        limit = int(request.data.get('limit') or domain.manual_reserve)
        batch = queue_inspection(domain, limit=limit, origin=BatchOrigin.API)

        response = Response(BatchSerializer(batch).data, status=status.HTTP_202_ACCEPTED)
        return self.store(request, response)
