"""Remplissage initial de la base à partir du corpus de recherche.

Enchaînement :

1. **Annexe** (`sec10.md`) — 52 textes avec statut, dates, cotation des
   libertés et pertinence Guadeloupe pour les lois promulguées.
2. **Citations** — index de résolution des marqueurs `[^dimXX-N^]` vers les
   sources vérifiables des douze rapports de dimension.
3. **Chapitres** (`sec00` → `sec09`) — résumés, points clés et marqueurs
   supplémentaires, rattachés phrase par phrase.
4. **Bibliographie** — filet de sécurité : toute loi promulguée reçoit les
   références consolidées qui citent son numéro.
5. **Analyses** (`insight.md`) — sept lectures transversales.

Rien n'est écrit qui ne vienne d'un fichier de `data/input/`, lequel n'est
jamais modifié. Le compte-rendu d'import indique fichier par fichier ce qui a
été lu, ce qui a été rattaché et ce qui manque.
"""

from __future__ import annotations

import argparse
import sqlite3
import time
from dataclasses import dataclass, field

from pipeline import chemins, db, guadeloupe, impacts, resolution_conflits
from pipeline.journal import configurer, logger
from pipeline.modeles import Source, Texte
from pipeline.parseurs import (
    analyses,
    bibliographie,
    chapitres,
    citations,
    echeances,
    tableaux_sec10,
)

log = logger("seed")

# Plancher fixé au §2 du cahier des charges.
TEXTES_ATTENDUS_MINIMUM = 49


@dataclass
class CompteRendu:
    """Ce que le seed a lu, écrit et manqué."""

    textes: int = 0
    sources: int = 0
    evenements: int = 0
    decisions_cc: int = 0
    analyses: int = 0
    echeances: int = 0

    textes_sans_source: list[str] = field(default_factory=list)
    textes_sans_resume: list[str] = field(default_factory=list)
    pertinences_deduites: list[str] = field(default_factory=list)
    impacts_deduits: list[str] = field(default_factory=list)
    verdicts_appliques: int = 0
    avertissements: list[str] = field(default_factory=list)

    marqueurs_avec_lien: int = 0
    marqueurs_sans_lien: int = 0
    marqueurs_non_resolus: int = 0

    def en_texte(self) -> str:
        lignes = [
            "Compte-rendu d'import du corpus de recherche",
            "=" * 44,
            f"  textes            : {self.textes}",
            f"  sources           : {self.sources}",
            f"  événements        : {self.evenements}",
            f"  décisions CC      : {self.decisions_cc}",
            f"  analyses          : {self.analyses}",
            f"  échéances         : {self.echeances}",
            "",
            "Résolution des citations",
            f"  avec lien         : {self.marqueurs_avec_lien}",
            f"  sans lien (réf.)  : {self.marqueurs_sans_lien}",
            f"  non résolus       : {self.marqueurs_non_resolus}",
            "",
            "Points d'attention",
            f"  textes sans source URL   : {len(self.textes_sans_source)}"
            + (f" → {', '.join(self.textes_sans_source)}" if self.textes_sans_source else ""),
            f"  textes sans résumé       : {len(self.textes_sans_resume)}"
            + (f" → {', '.join(self.textes_sans_resume)}" if self.textes_sans_resume else ""),
            f"  pertinences déduites     : {len(self.pertinences_deduites)}",
            f"  impacts déduits          : {len(self.impacts_deduits)}",
            f"  arbitrages appliqués     : {self.verdicts_appliques}",
        ]
        if self.avertissements:
            lignes.append("")
            lignes.append("Avertissements")
            lignes.extend(f"  ! {a}" for a in self.avertissements)
        return "\n".join(lignes)


def ensemencer(cx: sqlite3.Connection) -> CompteRendu:
    """Construit le jeu de données initial et l'écrit en base."""
    compte = CompteRendu()

    manquants = chemins.verifier_corpus()
    if manquants:
        compte.avertissements.extend(f"fichier d'entrée absent : {m}" for m in manquants)

    # 1. Annexe récapitulative — le socle factuel.
    annexe = tableaux_sec10.parser(chemins.chapitre(10).read_text(encoding="utf-8"))
    compte.avertissements.extend(annexe.avertissements)
    textes: dict[str, Texte] = {texte.id: texte for texte in annexe.textes}
    marqueurs_annexe = annexe.marqueurs

    # 2. Index de résolution des citations.
    repertoire = citations.construire()

    # 3. Chapitres rédigés.
    lecture = chapitres.parser()

    # 4. Enrichissement texte par texte.
    for texte in textes.values():
        _rattacher_sources(texte, repertoire, lecture, marqueurs_annexe)
        _rattacher_redaction(texte, lecture)
        _coter_guadeloupe(texte, compte)
        _ventiler_impacts(texte, lecture, compte)

        if not texte.sources:
            compte.textes_sans_source.append(texte.id)
        if not texte.resume:
            compte.textes_sans_resume.append(texte.id)
            texte.a_verifier = True
            texte.motif_verification = _ajouter_motif(
                texte.motif_verification,
                "Aucun paragraphe du rapport ne développe ce texte : "
                "seul l'essentiel de l'annexe est disponible.",
            )

    compte.marqueurs_avec_lien = len(repertoire.resolus_avec_lien)
    compte.marqueurs_sans_lien = len(repertoire.resolus_sans_lien)
    compte.marqueurs_non_resolus = len(repertoire.non_resolus)

    # 5. Arbitrages de la validation de phase 5 — le §7 du cahier des charges
    #    exige que la loi « Riposte » porte l'AFD à 500 € et non à 1 500 €.
    verdicts = resolution_conflits.lire_verdicts(
        chemins.PHASE5_VALIDATION.read_text(encoding="utf-8")
    )
    compte.verdicts_appliques = resolution_conflits.appliquer(textes, verdicts)

    # 6. Écriture.
    db.enregistrer_textes(cx, textes.values())

    lectures = analyses.parser(chemins.INSIGHTS.read_text(encoding="utf-8"))
    with db.transaction(cx):
        for analyse in lectures:
            db.enregistrer_insight(cx, analyse)
        for echeance in echeances.echeances_nationales():
            db.enregistrer_echeance(cx, echeance)

    statistiques = db.statistiques(cx)
    compte.textes = statistiques["textes"]
    compte.sources = statistiques["sources"]
    compte.evenements = statistiques["evenements"]
    compte.decisions_cc = statistiques["decisions_cc"]
    compte.analyses = statistiques["insights"]
    compte.echeances = statistiques["echeances"]

    if compte.textes < TEXTES_ATTENDUS_MINIMUM:
        compte.avertissements.append(
            f"{compte.textes} textes importés, {TEXTES_ATTENDUS_MINIMUM} attendus au minimum"
        )

    return compte


# ─────────────────────────────────────────────────────────────────────────────
# Enrichissement
# ─────────────────────────────────────────────────────────────────────────────
def _rattacher_sources(
    texte: Texte,
    repertoire: citations.Repertoire,
    lecture: chapitres.LectureChapitres,
    marqueurs_annexe: dict[str, list[str]],
) -> None:
    """Attache à un texte toutes les sources que le corpus lui rattache."""
    # Marqueurs portés par sa ligne d'annexe, puis par les chapitres.
    marqueurs = list(marqueurs_annexe.get(texte.id, []))
    marqueurs.extend(lecture.marqueurs_de(texte.id))

    sources = repertoire.resoudre_plusieurs(marqueurs)

    # Filet : la bibliographie consolidée. Trois clés d'accroche, du plus
    # spécifique au moins spécifique — un texte non promulgué n'a pas de numéro
    # de loi, sa seule identité est sa cote parlementaire (« AN n° 3025 »).
    affaires = [decision.numero_affaire for decision in texte.decisions_cc]
    documents = bibliographie.numeros_de_document(
        f"{texte.titre_court} {texte.titre_officiel or ''}"
    )
    sources.extend(
        repertoire.references_du_texte(
            numero_loi=texte.numero_officiel, affaires=affaires, documents=documents
        )
    )

    vues: set[str] = set()
    retenues: list[Source] = []
    for source in sources:
        if source.url in vues:
            continue
        vues.add(source.url)
        retenues.append(source)

    texte.sources = retenues


def _rattacher_redaction(texte: Texte, lecture: chapitres.LectureChapitres) -> None:
    """Reprend résumé et points clés du rapport, sans les reformuler."""
    if resume := lecture.resume_de(texte.id):
        texte.resume = resume

    # Les points clés de l'annexe (colonne « Enjeu libertés ») sont déjà posés
    # par le parseur de tableaux ; on complète avec la note de régime.
    if texte.regime_libertes_note and texte.regime_libertes_note not in texte.points_cles:
        texte.points_cles.append(texte.regime_libertes_note)
    if texte.guadeloupe_note and texte.guadeloupe_note not in texte.points_cles:
        texte.points_cles.append(texte.guadeloupe_note)


def _coter_guadeloupe(texte: Texte, compte: CompteRendu) -> None:
    """Cote la pertinence guadeloupéenne des textes que l'annexe ne cote pas.

    Les 27 lois promulguées portent la cotation manuelle du rapport : elle est
    conservée telle quelle, conformément au §5.3 du cahier des charges.
    """
    if texte.guadeloupe_pertinence is not None:
        return

    cotation = guadeloupe.coter(
        texte.titre_court,
        texte.titre_officiel,
        texte.resume,
        " ".join(texte.points_cles),
        texte.guadeloupe_note,
    )
    if cotation.pertinence is None:
        return

    texte.guadeloupe_pertinence = cotation.pertinence
    texte.guadeloupe_note = texte.guadeloupe_note or cotation.note
    texte.a_verifier = True
    texte.motif_verification = _ajouter_motif(
        texte.motif_verification, guadeloupe.MOTIF_VERIFICATION
    )
    compte.pertinences_deduites.append(texte.id)


def _ventiler_impacts(
    texte: Texte, lecture: chapitres.LectureChapitres, compte: CompteRendu
) -> None:
    """Répartit l'effet du texte entre les trois publics suivis."""
    ventilation = impacts.ventiler(texte.id, lecture, regime=texte.regime_libertes)
    if not ventilation.impacts:
        return

    texte.impacts = ventilation.impacts
    if ventilation.deduit:
        texte.a_verifier = True
        texte.motif_verification = _ajouter_motif(
            texte.motif_verification, impacts.MOTIF_VERIFICATION
        )
        compte.impacts_deduits.append(texte.id)


def _ajouter_motif(existant: str | None, nouveau: str) -> str:
    if not existant:
        return nouveau
    if nouveau in existant:
        return existant
    return f"{existant} {nouveau}"


# ─────────────────────────────────────────────────────────────────────────────
# Ligne de commande
# ─────────────────────────────────────────────────────────────────────────────
def main() -> None:
    analyseur = argparse.ArgumentParser(
        description="Remplit la base à partir du corpus de recherche de data/input/."
    )
    analyseur.add_argument(
        "--repartir-de-zero",
        action="store_true",
        help="supprime la base existante avant l'import",
    )
    analyseur.add_argument("--verbeux", action="store_true", help="journalisation détaillée")
    arguments = analyseur.parse_args()

    configurer("DEBUG" if arguments.verbeux else "INFO")
    depart = time.monotonic()

    cx = db.initialiser(repartir_de_zero=arguments.repartir_de_zero)
    run_id = db.ouvrir_run(cx, "seed")
    compte = ensemencer(cx)
    duree = time.monotonic() - depart

    db.cloturer_run(
        cx,
        run_id,
        duree_s=duree,
        ajouts=compte.textes,
        echecs=len(compte.avertissements),
        alertes=compte.avertissements,
        rapport=compte.en_texte(),
    )

    print()
    print(compte.en_texte())
    print()
    log.info(
        "seed terminé",
        duree_s=round(duree, 2),
        textes=compte.textes,
        sources=compte.sources,
        base=str(chemins.BASE_SQLITE),
    )

    if compte.textes < TEXTES_ATTENDUS_MINIMUM:
        raise SystemExit(1)


if __name__ == "__main__":
    main()
