-- 019_status_constraints_and_retention.sql
--
-- A) Contraintes de domaine sur les statuts
--
-- Dix-huit migrations, zéro CHECK. crawl_run.status, job_triggers.job_type et
-- band_locations.geocode_status sont du texte libre dont les valeurs admises ne
-- vivaient que dans des commentaires. C'est le dénominateur commun de plusieurs
-- bugs de ce dépôt : un statut 'error' que plus personne n'écrivait mais qu'un
-- bouton d'admin ciblait encore, des lignes 'processing' qu'aucune requête ne
-- reprenait, un job_type réclamé par le mauvais daemon. Une faute de frappe
-- écrivait une ligne durablement invisible, sans jamais rien faire échouer.
--
-- Les valeurs sont relevées sur le code qui écrit réellement ces colonnes
-- (crawler/src/db.py, geocoder/src/*.py, api/src/adminRoutes.js).
--
-- NOT VALID est délibéré et DÉFINITIF ici : la contrainte s'applique à toutes
-- les écritures futures, mais l'historique n'est pas relu. Cette migration
-- s'exécute au démarrage de chaque conteneur API ; valider exigerait un scan
-- complet, et une seule ligne héritée hors domaine ferait échouer la migration,
-- donc le démarrage de l'API. Le but est d'arrêter la dérive, pas de réécrire
-- le passé. Pour valider un jour, à froid :
--   ALTER TABLE crawl_run VALIDATE CONSTRAINT crawl_run_status_check;

ALTER TABLE crawl_run DROP CONSTRAINT IF EXISTS crawl_run_status_check;
ALTER TABLE crawl_run ADD CONSTRAINT crawl_run_status_check
  CHECK (status IN ('running', 'done', 'error', 'cancelled')) NOT VALID;

ALTER TABLE job_triggers DROP CONSTRAINT IF EXISTS job_triggers_status_check;
ALTER TABLE job_triggers ADD CONSTRAINT job_triggers_status_check
  CHECK (status IN ('pending', 'running', 'done', 'error', 'cancelled')) NOT VALID;

ALTER TABLE job_triggers DROP CONSTRAINT IF EXISTS job_triggers_type_check;
ALTER TABLE job_triggers ADD CONSTRAINT job_triggers_type_check
  CHECK (job_type IN ('enrich', 'incremental', 'full_crawl', 'geocoder_enqueue')) NOT VALID;

ALTER TABLE band_locations DROP CONSTRAINT IF EXISTS band_locations_geocode_status_check;
ALTER TABLE band_locations ADD CONSTRAINT band_locations_geocode_status_check
  CHECK (geocode_status IN (
    'queued', 'processing', 'done', 'country_only', 'error', 'llm_needed', 'manual'
  )) NOT VALID;

-- ------------------------------------------------------------------
-- B) Table morte
--
-- geocode_queue date du pipeline Nominatim, remplacé par band_locations. Plus
-- aucune ligne de code ne la référence ; elle ne subsistait que dans le schéma.
-- ------------------------------------------------------------------
DROP TABLE IF EXISTS geocode_queue;

-- ------------------------------------------------------------------
-- C) Rétention
--
-- crawl_log, admin_audit_log et admin_login_attempts grossissent sans fin. La
-- migration 015 avait pourtant explicitement conçu service_health pour éviter
-- « une table qui grossit » — la leçon n'avait pas été appliquée aux autres.
-- crawl_log prend une ligne par échec d'enrichissement, et GET /admin/api/activity
-- relit admin_audit_log en entier à chaque affichage.
--
-- La purge n'est pas automatique (pas de pg_cron ici) : elle est exposée par
-- l'API, qui l'appelle au démarrage. Les durées sont volontairement longues,
-- l'objectif est de borner la croissance, pas d'effacer l'historique utile.
-- ------------------------------------------------------------------
CREATE OR REPLACE FUNCTION purge_historique(
  jours_logs    INT DEFAULT 90,
  jours_audit   INT DEFAULT 365,
  jours_tentatives INT DEFAULT 30
) RETURNS TABLE (table_purgee TEXT, lignes BIGINT) AS $$
BEGIN
  RETURN QUERY
  WITH d AS (
    DELETE FROM crawl_log WHERE created_at < now() - make_interval(days => jours_logs)
    RETURNING 1
  ) SELECT 'crawl_log'::TEXT, count(*) FROM d;

  RETURN QUERY
  WITH d AS (
    DELETE FROM admin_audit_log WHERE created_at < now() - make_interval(days => jours_audit)
    RETURNING 1
  ) SELECT 'admin_audit_log'::TEXT, count(*) FROM d;

  RETURN QUERY
  WITH d AS (
    DELETE FROM admin_login_attempts WHERE created_at < now() - make_interval(days => jours_tentatives)
    RETURNING 1
  ) SELECT 'admin_login_attempts'::TEXT, count(*) FROM d;
END;
$$ LANGUAGE plpgsql;

-- Index manquant : la purge et le filtre `since` de /admin/api/logs balayaient
-- crawl_log en entier faute d'index sur created_at seul (celui de la 006 est
-- DESC, utilisable, mais on garde l'intention explicite ici).
CREATE INDEX IF NOT EXISTS idx_admin_login_attempts_created ON admin_login_attempts (created_at);
CREATE INDEX IF NOT EXISTS idx_admin_audit_log_created ON admin_audit_log (created_at);
