"""Blocs sourcés, notes, bibliographie et résolution des marqueurs."""

from __future__ import annotations

from datetime import date

import pytest

from pipeline import chemins
from pipeline.modeles import Confiance
from pipeline.parseurs import bibliographie, blocs_claim, citations, notes_bas_page
from pipeline.parseurs.markdown import extraire_marqueurs

# ─────────────────────────────────────────────────────────────────────────────
# Blocs sourcés — les quatre dialectes du corpus
# ─────────────────────────────────────────────────────────────────────────────

DIALECTE_A = """\
Claim: La loi n° 2026-350 est publiée au JORF du 10 mai 2026 [^1^].
Source: Légifrance
URL: https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000054049780
Date: 10 mai 2026
Excerpt: « visant à garantir le droit de visite »
Confidence: high
"""

DIALECTE_B = """\
**Claim 3 :** Le gouvernement a élargi le texte par lettre rectificative [^3^]
Source: DHnet / AFP
URL: https://www.dhnet.be/exemple
Date: 1er juillet 2026
Confidence: high
"""

DIALECTE_C = """\
**Claim 1.2** — Le Conseil d'État confirme que le texte comporte 23 articles.
- **Source** : Conseil d'État
- **URL** : https://www.conseil-etat.fr/avis ; https://www.senat.fr/rap/exemple.html
- **Date** : 2 avril 2026
- **Confidence** : Haute
"""

DIALECTE_PHASE5 = """\
**Claim 1.1 (décisif, source primaire) : le texte fixe l'AFD à 500 €.**
- **Source :** Assemblée nationale, TEXTE ADOPTÉ n° 340
- **URL :** https://www.assemblee-nationale.fr/dyn/17/textes/l17t0340_texte-adopte-provisoire
- **Date :** 21/07/2026
- **Confidence : très élevée**
"""


@pytest.mark.parametrize(
    ("source", "urls_attendues"),
    [
        (DIALECTE_A, 1),
        (DIALECTE_B, 1),
        (DIALECTE_C, 2),      # deux URLs séparées par « ; » sur la même ligne
        (DIALECTE_PHASE5, 1),
    ],
)
def test_les_quatre_dialectes_sont_lus(source: str, urls_attendues: int) -> None:
    (bloc,) = blocs_claim.parser(source, "dimXX")
    assert len(bloc.urls) == urls_attendues
    assert bloc.confiance is Confiance.HIGH


def test_dialecte_a_complet() -> None:
    (bloc,) = blocs_claim.parser(DIALECTE_A, "dim01")
    assert bloc.marqueurs == ["1"]
    assert bloc.editeur == "Légifrance"
    assert bloc.date_publication == date(2026, 5, 10)
    assert bloc.extrait == "visant à garantir le droit de visite"
    (source,) = bloc.en_sources()
    assert source.tier == "T1"        # legifrance.gouv.fr reconnu comme primaire
    assert source.marqueur == "dim01-1"


def test_phase5_sans_la_parenthese_ne_serait_pas_lu() -> None:
    """La forme « **Claim 1.1 (décisif…) : … » est propre à la validation."""
    blocs = blocs_claim.parser(DIALECTE_PHASE5, "phase5")
    assert len(blocs) == 1
    assert blocs[0].confiance is Confiance.HIGH


@pytest.mark.parametrize(
    ("valeur", "attendu"),
    [
        ("high", Confiance.HIGH),
        ("Haute", Confiance.HIGH),
        ("très élevée", Confiance.HIGH),
        ("élevée", Confiance.HIGH),
        ("medium", Confiance.MEDIUM),
        ("medium-high", Confiance.MEDIUM),
        ("low", Confiance.LOW),
        ("valeur inconnue", Confiance.MEDIUM),
    ],
)
def test_lecture_de_la_confiance(valeur: str, attendu: Confiance) -> None:
    assert blocs_claim.lire_confiance(valeur) is attendu


def test_tous_les_rapports_de_dimension_rendent_des_blocs() -> None:
    """Aucune dimension ne doit être muette : ce serait un dialecte manqué."""
    for numero in range(1, 13):
        markdown = chemins.dimension(numero).read_text(encoding="utf-8")
        blocs = blocs_claim.parser(markdown, f"dim{numero:02d}")
        assert blocs, f"dim{numero:02d} n'a produit aucun bloc"


# ─────────────────────────────────────────────────────────────────────────────
# Notes de bas de page
# ─────────────────────────────────────────────────────────────────────────────


def test_note_avec_url() -> None:
    ligne = "[^1141^] senat.fr — Texte n° 890 (09/07/2026) — https://www.senat.fr/leg/pjl25-890.html"
    (note,) = notes_bas_page.parser(ligne, "dim09")
    assert note.numero == "1141"
    assert note.url == "https://www.senat.fr/leg/pjl25-890.html"
    assert note.editeur == "senat.fr"
    assert note.date_publication == date(2026, 7, 9)
    assert note.en_source() is not None


def test_note_sans_url_reste_sans_source() -> None:
    (note,) = notes_bas_page.parser("[^44^] diplomatie.gouv.fr, 16/04/2026.", "dim01")
    assert note.url is None
    assert note.en_source() is None
    assert note.texte  # le libellé est conservé


def test_url_legifrance_reconstruite_depuis_un_identifiant_jorf() -> None:
    """Le schéma d'URL de Légifrance est fixe : le reconstruire n'invente rien."""
    (note,) = notes_bas_page.parser(
        "[^1^] Légifrance, JORFTEXT000054049780 (loi 2026-350).", "dim01"
    )
    assert note.url == "https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000054049780"
    assert note.url_reconstruite is True


# ─────────────────────────────────────────────────────────────────────────────
# Bibliographie
# ─────────────────────────────────────────────────────────────────────────────


def test_bibliographie_entierement_liee() -> None:
    references = bibliographie.parser(chemins.BIBLIOGRAPHIE.read_text(encoding="utf-8"))
    assert len(references) == 481
    assert all(r.url for r in references), "la bibliographie doit être intégralement liée"


def test_numero_de_loi_translittere() -> None:
    (reference,) = bibliographie.parser(
        "[1] Aida. Loi ndeg 2026 554 290626 visant a relancer [EB/OL]. 29 juin 2026. https://x.fr/a"
    )
    assert "2026-554" in reference.numeros_cites


def test_les_plages_de_pages_ne_font_pas_des_numeros_de_loi() -> None:
    (reference,) = bibliographie.parser(
        "[2] Sénat. Programmation militaire 2024-2030 [EB/OL]. 2026. https://senat.fr/x"
    )
    assert "2024-2030" not in reference.numeros_cites


def test_cotes_de_documents_parlementaires() -> None:
    assert bibliographie.numeros_de_document("L17b3025 proposition loi") == ["3025"]
    assert bibliographie.numeros_de_document("ppl25-691") == ["691"]
    assert bibliographie.numeros_de_document("Texte n° 890") == ["890"]


def test_table_de_correspondance_des_prefixes() -> None:
    """Les plages « 5–6 » doivent être dépliées."""
    correspondances = bibliographie.lire_table_de_correspondance(
        chemins.BIBLIOGRAPHIE.read_text(encoding="utf-8")
    )
    assert set(correspondances) >= {f"dim{n:02d}" for n in range(1, 13)} | {"phase5"}
    assert 5 in correspondances["dim01"] and 6 in correspondances["dim01"]


# ─────────────────────────────────────────────────────────────────────────────
# Résolution des marqueurs
# ─────────────────────────────────────────────────────────────────────────────


@pytest.fixture(scope="module")
def repertoire() -> citations.Repertoire:
    return citations.construire()


def test_tous_les_marqueurs_du_rapport_sont_resolus(repertoire) -> None:
    """Aucun marqueur du rapport ne doit rester orphelin."""
    repertoire.remettre_a_zero_diagnostic()

    marqueurs = {}
    for numero in range(11):
        for marqueur in extraire_marqueurs(
            chemins.chapitre(numero).read_text(encoding="utf-8")
        ):
            marqueurs[marqueur.brut] = marqueur

    for marqueur in marqueurs.values():
        repertoire.resoudre(marqueur)

    assert repertoire.non_resolus == []
    assert repertoire.taux_de_resolution == 1.0
    # La très grande majorité doit aboutir à une URL, pas seulement à un libellé.
    assert len(repertoire.resolus_avec_lien) / len(marqueurs) > 0.90


def test_resolution_d_un_marqueur_de_dimension(repertoire) -> None:
    source = repertoire.resoudre("dim07-24")
    assert source is not None
    assert source.url.startswith("http")
    assert source.marqueur == "dim07-24"


def test_marqueurs_insight_sans_source_par_construction(repertoire) -> None:
    """La bibliographie indique qu'insight n'a « aucune source primaire propre »."""
    repertoire.remettre_a_zero_diagnostic()
    assert repertoire.resoudre("insight-5") is None
    assert repertoire.non_resolus == []


def test_notes_sans_lien_rapprochees_de_la_bibliographie(repertoire) -> None:
    assert repertoire.rapprochements > 0
