"""Validation ATIH des codes extraits.

Prend un JSON d'extraction produit par `pipeline/extract.py` et l'enrichit
d'une section `_validation` par champ de code médical (CIM-10, CCAM, GHM, GHS)
avec :

- `valid`  : le code existe dans le référentiel ATIH 2018
- `suggestion` : si invalide, code le plus proche par Levenshtein ≤ 1 (CIM-10)
- `libelle_ref` : libellé officiel ATIH (CIM-10) pour audit

Plus des cross-checks (GHS ∈ ghm_to_ghs(GHM)) pour détecter des incohérences
de groupage.

Principes :
- Lecture seule sur le JSON source — on produit une COPIE enrichie.
- Ne supprime / ne corrige RIEN automatiquement ; seule une suggestion est
  annotée. La correction reste à la discrétion d'un humain (overlay) ou d'un
  prochain pass automatique.
"""
from __future__ import annotations

from copy import deepcopy
from typing import Any

from .referentials import (
    get_cim10_libelle,
    ghm_to_ghs,
    is_valid_ccam,
    is_valid_cim10,
    is_valid_ghm,
    is_valid_ghs,
    nearest_cim10,
)


# ============================================================
# Helpers
# ============================================================

def _check_cim10(code: str) -> dict:
    """Valide un code CIM-10 et suggère une correction si invalide."""
    code = (code or "").strip()
    if not code:
        return {"code": "", "valid": None}
    valid = is_valid_cim10(code)
    entry = {"code": code, "valid": valid}
    if valid:
        entry["libelle_ref"] = get_cim10_libelle(code)
    else:
        sug = nearest_cim10(code, max_distance=1)
        if sug:
            entry["suggestion"] = sug
            entry["suggestion_libelle"] = get_cim10_libelle(sug)
    return entry


def _check_ccam(code: str) -> dict:
    code = (code or "").strip()
    if not code:
        return {"code": "", "valid": None}
    return {"code": code, "valid": is_valid_ccam(code)}


def _check_ghm(code: str) -> dict:
    code = (code or "").strip()
    if not code:
        return {"code": "", "valid": None}
    entry = {"code": code, "valid": is_valid_ghm(code)}
    if entry["valid"]:
        entry["ghs_possibles"] = ghm_to_ghs(code)
    return entry


def _check_ghs(code: str) -> dict:
    code = (code or "").strip()
    if not code:
        return {"code": "", "valid": None}
    return {"code": code, "valid": is_valid_ghs(code)}


# ============================================================
# Validation d'un bloc codage (etab ou reco)
# ============================================================

def _validate_codage(codage: dict) -> dict:
    """Valide un bloc codage_etab ou codage_reco."""
    if not isinstance(codage, dict):
        return {}
    out = {
        "dp": _check_cim10(codage.get("dp", "")),
        "dr": _check_cim10(codage.get("dr", "")),
    }
    das_list = codage.get("das") or []
    if isinstance(das_list, list):
        out["das"] = [_check_cim10(d.get("code", "")) if isinstance(d, dict) else _check_cim10(str(d))
                      for d in das_list]
    return out


def _validate_actes(actes: Any) -> list[dict]:
    if not isinstance(actes, list):
        return []
    return [_check_ccam(a.get("code", "")) if isinstance(a, dict) else _check_ccam(str(a))
            for a in actes]


# ============================================================
# Cross-checks GHM ↔ GHS
# ============================================================

def _cross_check_ghm_ghs(ghm: str, ghs: str) -> dict:
    """Vérifie qu'un GHS observé est listé parmi les GHS possibles du GHM."""
    ghm = (ghm or "").strip()
    ghs = (ghs or "").strip()
    if not ghm or not ghs:
        return {"checked": False, "reason": "ghm ou ghs manquant"}
    if not is_valid_ghm(ghm):
        return {"checked": False, "reason": "GHM invalide"}
    possibles = ghm_to_ghs(ghm)
    # Normalisation simple : on compare la fin (au cas où l'un est tronqué)
    ok = ghs in possibles or any(p.endswith(ghs) or ghs.endswith(p) for p in possibles)
    return {
        "checked": True,
        "coherent": ok,
        "ghs_extrait": ghs,
        "ghs_possibles": possibles,
    }


# ============================================================
# Point d'entrée
# ============================================================

def validate_recueil(recueil: dict) -> dict:
    """Retourne un dict résumé des validations pour la page recueil."""
    v = {
        "codage_etab": _validate_codage(recueil.get("codage_etab", {})),
        "codage_reco": _validate_codage(recueil.get("codage_reco", {})),
        "actes_etab": _validate_actes(recueil.get("actes_etab", [])),
        "actes_reco": _validate_actes(recueil.get("actes_reco", [])),
        "ghm_etab": _check_ghm(recueil.get("ghm_etab", "")),
        "ghs_etab": _check_ghs(recueil.get("ghs_etab", "")),
        "ghm_reco": _check_ghm(recueil.get("ghm_reco", "")),
        "ghs_reco": _check_ghs(recueil.get("ghs_reco", "")),
        "cross_checks": {
            "etab": _cross_check_ghm_ghs(
                recueil.get("ghm_etab", ""), recueil.get("ghs_etab", "")),
            "reco": _cross_check_ghm_ghs(
                recueil.get("ghm_reco", ""), recueil.get("ghs_reco", "")),
        },
    }
    v["summary"] = _summarize(v)
    return v


def _summarize(validation: dict) -> dict:
    """Compte les codes valides / invalides dans une section _validation."""
    valid, invalid, empty = 0, 0, 0

    def _count_entry(e):
        nonlocal valid, invalid, empty
        if e.get("valid") is True: valid += 1
        elif e.get("valid") is False: invalid += 1
        else: empty += 1

    for section in ("codage_etab", "codage_reco"):
        sec = validation.get(section, {}) or {}
        _count_entry(sec.get("dp", {}))
        _count_entry(sec.get("dr", {}))
        for d in sec.get("das", []) or []:
            _count_entry(d)
    for actes_key in ("actes_etab", "actes_reco"):
        for a in validation.get(actes_key, []) or []:
            _count_entry(a)
    for g in ("ghm_etab", "ghs_etab", "ghm_reco", "ghs_reco"):
        _count_entry(validation.get(g, {}))

    cc = validation.get("cross_checks", {})
    incoherent = sum(1 for v in cc.values() if v.get("checked") and not v.get("coherent"))
    return {
        "valid": valid, "invalid": invalid, "empty": empty,
        "total_codes": valid + invalid,
        "ghm_ghs_incoherents": incoherent,
    }


def annotate(extraction: dict) -> dict:
    """Annote un JSON d'extraction complet avec validation ATIH.

    Retourne une COPIE enrichie d'un bloc `_validation` à la racine de chaque
    page structurée. N'efface / ne corrige aucune valeur.
    """
    out = deepcopy(extraction)
    ext = out.get("extraction") or {}
    if "recueil" in ext and isinstance(ext["recueil"], dict):
        ext["recueil"]["_validation"] = validate_recueil(ext["recueil"])
    # Concertation 2 : valider les 3 GHS
    if "concertation_2" in ext and isinstance(ext["concertation_2"], dict):
        c2 = ext["concertation_2"]
        c2["_validation"] = {
            "ghs_initial":            _check_ghs(c2.get("ghs_initial", "")),
            "ghs_avant_concertation": _check_ghs(c2.get("ghs_avant_concertation", "")),
            "ghs_final":              _check_ghs(c2.get("ghs_final", "")),
        }
    return out


if __name__ == "__main__":
    # Test rapide sur OGC 7
    import json, sys
    path = sys.argv[1] if len(sys.argv) > 1 else "output/v2/OGC 7.json"
    with open(path) as f:
        data = json.load(f)
    annotated = annotate(data)
    rec_v = annotated["extraction"]["recueil"]["_validation"]
    print(json.dumps(rec_v["summary"], indent=2))
    print("\ncross_checks:", json.dumps(rec_v["cross_checks"], indent=2, ensure_ascii=False))
