"""Historial de cobertura, con el dato crudo de Google al lado del traducido."""

from django.db import models
from django.utils import timezone

from apps.core.models import BaseModel


class CoverageRecord(BaseModel):
    """
    Una lectura de cobertura de Google, guardada tal como llegó.

    Se escribe **sólo cuando el estado cambia** respecto del registro anterior,
    más la primera lectura de cada URL. Un sitio de veinte mil URLs consultadas a
    diario generaría siete millones de filas al año casi todas idénticas; lo que
    interesa es cuándo cambió algo.

    Los campos crudos no son redundancia: son la prueba. `fetched_at` es
    obligatorio porque un estado de indexación sin la fecha en que Google lo
    dijo no es un dato, es una impresión (principio I). Y guardar el veredicto
    original permite recalcular la traducción si mañana entendemos mejor un
    estado, sin gastar cuota volviendo a preguntar.
    """

    url = models.ForeignKey('sitemaps.Url', on_delete=models.CASCADE, related_name='records')
    batch = models.ForeignKey(
        'jobs.Batch', on_delete=models.SET_NULL, null=True, blank=True, related_name='records'
    )
    state = models.CharField(max_length=25)
    fetched_at = models.DateTimeField()

    raw_verdict = models.CharField(max_length=50, blank=True, default='')
    raw_coverage_state = models.CharField(max_length=255, blank=True, default='')
    raw_robots_state = models.CharField(max_length=50, blank=True, default='')
    raw_indexing_state = models.CharField(max_length=50, blank=True, default='')
    raw_page_fetch_state = models.CharField(max_length=50, blank=True, default='')
    google_canonical = models.URLField(max_length=2000, blank=True, default='')
    user_canonical = models.URLField(max_length=2000, blank=True, default='')
    last_crawl_time = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ['-fetched_at']
        indexes = [models.Index(fields=['url', '-fetched_at'])]

    def __str__(self) -> str:
        return f'{self.url_id} {self.state} @ {self.fetched_at:%Y-%m-%d}'


class CoverageExport(BaseModel):
    """
    Un archivo de cobertura pedido, que se arma en segundo plano (FR-058).

    Existe sólo para las exportaciones que no entran en una respuesta HTTP. Por
    debajo del umbral el CSV sale en la misma petición y no queda rastro de
    nada: guardar una fila por cada descarga chica sería inventar historia de un
    hecho que se agota en el momento.

    `filters` guarda el recorte con el que se pidió. Es la única manera de que,
    al bajar el archivo tres horas después, se pueda saber qué contiene: un CSV
    de 128.000 filas sin la constancia de qué filtro lo produjo no se puede
    verificar contra nada.
    """

    batch = models.OneToOneField('jobs.Batch', on_delete=models.CASCADE, related_name='export')
    #: El recorte de la tabla, tal como lo saneó `COVERAGE_TABLE`.
    filters = models.JSONField(default=dict, blank=True)
    row_count = models.PositiveIntegerField(default=0)
    size_bytes = models.PositiveBigIntegerField(default=0)
    #: Hasta cuándo se puede bajar. Nulo mientras el archivo no exista todavía.
    expires_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self) -> str:
        return f'exportación {self.id} ({self.row_count} filas)'

    @property
    def file_name(self) -> str:
        """
        Cómo se llama el archivo en disco.

        Sale del identificador y no de nada que venga de afuera: un nombre
        armado con el hostname o con el filtro sería una dirección de archivo
        construida con datos del usuario, que es como se llega a leer un archivo
        de otra carpeta.
        """
        return f'{self.id}.csv'

    @property
    def is_expired(self) -> bool:
        return self.expires_at is not None and self.expires_at <= timezone.now()
