"""
Endpoint por el que un despliegue propone qué reindexar.

Es el camino 2 de la task: la landing —u otro repositorio— manda después de un
despliegue la lista de páginas que cambiaron, y la aplicación arma con eso un
lote **en borrador**.

**Nunca se ejecuta sin que una persona confirme.** Ésa es la regla que define
este camino, y la que explica por qué contesta `201` y no `202`: no se encoló
nada. Se creó algo que espera. Un `202` diría «lo estamos haciendo», que es
exactamente lo que no está pasando.

La respuesta incluye qué entró y qué se descartó, con el motivo. Quien integra
esto lo lee en su registro de despliegue y tiene que poder contestar «mandé
cuarenta, entraron treinta y siete» sin abrir la pantalla.
"""

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 ValidationFailed
from apps.domains.api import company_domain_or_error
from apps.indexing.models import IndexingRequest
from apps.indexing.services import create_batch, eligible_urls
from apps.jobs.models import BatchOrigin

#: Cuántas direcciones acepta un pedido.
#:
#: No es la cuota de Google —la aplicación no lleva contador propio de eso, a
#: propósito—, es el tamaño de una petición HTTP razonable. Un despliegue que
#: cambió más de mil páginas es una migración, y eso se resuelve resincronizando
#: el sitemap y no enumerando direcciones en un cuerpo JSON.
MAX_URLS_PER_REQUEST = 1000


class IndexingRequestSerializer(serializers.ModelSerializer):
    class Meta:
        model = IndexingRequest
        fields = [
            'id',
            'loc',
            'position',
            'state',
            'sent_at',
            'request_body',
            'response_status',
            'response_body',
            'error_code',
        ]


class IndexingBatchCreateSerializer(serializers.Serializer):
    urls = serializers.ListField(child=serializers.CharField(), allow_empty=False)


class DomainIndexingBatchView(BaseApiView):
    @extend_schema(
        request=IndexingBatchCreateSerializer,
        responses={201: IndexingRequestSerializer(many=True)},
        tags=['indexing'],
    )
    def post(self, request, domain_id):
        """
        Crea un lote de indexación en borrador con las direcciones que se manden.

        Descarta dos clases de dirección y las informa por separado, porque se
        resuelven distinto: una ajena a la propiedad nunca va a entrar, y una que
        salió del sitemap puede volver a entrar mañana.
        """
        replay = self.replay(request)
        if replay is not None:
            return replay

        domain = company_domain_or_error(domain_id)

        locs = request.data.get('urls')
        if not isinstance(locs, list) or not locs:
            raise ValidationFailed('Mandá una lista de direcciones en «urls».', field='urls')

        if len(locs) > MAX_URLS_PER_REQUEST:
            raise ValidationFailed(
                f'Un pedido admite hasta {MAX_URLS_PER_REQUEST} direcciones.',
                field='urls',
                limit=MAX_URLS_PER_REQUEST,
                received=len(locs),
            )

        accepted, discarded = eligible_urls(domain, locs)
        if not accepted:
            raise ValidationFailed(
                'Ninguna de las direcciones que mandaste pertenece a esta propiedad o sigue '
                'vigente en el sitemap.',
                field='urls',
                discarded=discarded,
            )

        batch = create_batch(domain, accepted, origin=BatchOrigin.API)

        response = Response(
            {
                'batch_id': str(batch.id),
                'state': batch.state,
                'accepted': len(accepted),
                # Lo descartado viaja siempre, también vacío: quien lo lee en un
                # registro de despliegue no tiene que distinguir «no hubo
                # descartes» de «esta versión no los informa».
                'discarded': discarded,
                'requests': IndexingRequestSerializer(
                    batch.indexing_requests.order_by('position'), many=True
                ).data,
            },
            status=status.HTTP_201_CREATED,
        )
        return self.store(request, response)


class IndexingBatchDetailView(BaseApiView):
    @extend_schema(responses=IndexingRequestSerializer(many=True), tags=['indexing'])
    def get(self, request, batch_id):
        """
        La evidencia del lote: qué se mandó y qué contestó Google, crudo.

        Es el endpoint que hace verificable el producto desde afuera. Devuelve
        los cuerpos tal como quedaron guardados, sin resumir ni traducir.
        """
        from django.http import Http404

        from apps.jobs.models import Batch, BatchKind

        batch = (
            Batch.objects.filter(
                id=batch_id, kind=BatchKind.URL_INDEXING, domain__deactivated_at__isnull=True
            )
            .select_related('domain')
            .first()
        )
        if batch is None:
            raise Http404('Ese lote de indexación no existe en esta cuenta.')

        return Response(
            {
                'batch_id': str(batch.id),
                'state': batch.state,
                'requests': IndexingRequestSerializer(
                    batch.indexing_requests.order_by('position'), many=True
                ).data,
            }
        )
