"""
El aviso a la landing de que cambió el catálogo del generador.

La landing guarda una copia del catálogo y de sus fotos. Cuando Tools se
despliega con un cambio, este aviso le dice que vuelva a pedirlo; su cron
horario cubre el aviso que no llegue (decisión de la owner, 2026-10-08).

El aviso sólo lleva la versión: la landing pide el catálogo por la API, con su
clave, como siempre. Así el aviso no es un segundo camino por el que viaja el
catálogo, y uno falso no puede meterle nada: como mucho la hace preguntar.

Va firmado igual: HMAC-SHA256 de `<timestamp>.<cuerpo>` con el secreto
compartido. El timestamp entra en la firma para que la landing pueda rechazar
un aviso viejo repetido.
"""

import hashlib
import hmac
import json
import time
from dataclasses import dataclass

import httpx
from django.conf import settings

from apps.ai import website

TIMEOUT_SECONDS = 10.0

TIMESTAMP_HEADER = 'X-Tools-Timestamp'
SIGNATURE_HEADER = 'X-Tools-Signature'


class WebhookNotConfigured(Exception):
    """Falta la dirección o el secreto: no hay a quién avisar, o no se puede firmar."""


@dataclass(frozen=True)
class Delivery:
    version: str
    status_code: int


def sign(secret: str, timestamp: str, body: bytes) -> str:
    """La firma que la landing recalcula. `sha256=` adelante, como la de GitHub."""
    message = timestamp.encode() + b'.' + body
    return 'sha256=' + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()


def notify_catalog(*, client: httpx.Client | None = None) -> Delivery:
    """
    Avisa a la landing la versión vigente del catálogo.

    Avisa siempre, haya cambiado o no: Tools no recuerda qué avisó la última
    vez, y no le hace falta. Si la versión es la que la landing ya tiene, su
    pedido del catálogo vuelve con `304` y no hace nada.

    Levanta `httpx.HTTPError` si la landing no contesta o contesta con error:
    quien lo corre —el deploy— tiene que enterarse.
    """
    url = settings.LANDING_CATALOG_WEBHOOK_URL
    secret = settings.LANDING_WEBHOOK_SECRET
    if not url or not secret:
        raise WebhookNotConfigured()

    version = website.catalog_version()
    body = json.dumps({'version': version}).encode()
    timestamp = str(int(time.time()))
    headers = {
        'Content-Type': 'application/json',
        TIMESTAMP_HEADER: timestamp,
        SIGNATURE_HEADER: sign(secret, timestamp, body),
    }

    client = client or httpx.Client(timeout=TIMEOUT_SECONDS)
    with client:
        response = client.post(url, content=body, headers=headers)
    response.raise_for_status()
    return Delivery(version=version, status_code=response.status_code)
