#!/usr/bin/env python3
"""Recette hors ligne de quatre champs plats, Python 3.9+, sans dépendance.

Compare mentionText à une vérité terrain relue. Ce script ne valide pas une
facture inconnue et ne constitue pas une autorisation d'écriture dans un ERP.
Codes de sortie : 0 conforme au référentiel, 1 écarts, 2 entrée invalide.
"""

import json
import sys
import unicodedata
from pathlib import Path

FIELDS = {"document_number", "invoice_date", "total_ht", "currency"}


def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("Clé JSON répétée : " + key)
        result[key] = value
    return result


def load_json(path):
    return json.loads(Path(path).read_text(encoding="utf-8"),
                      object_pairs_hook=unique_object)


def comparable(value):
    # Aucun arrondi, conversion numérique, changement de casse ou de date.
    return unicodedata.normalize("NFC", value).strip()


def evaluate(expected, response):
    if not isinstance(expected, dict) or set(expected) != FIELDS:
        raise ValueError("Le référentiel doit contenir exactement les quatre champs.")
    for value in expected.values():
        if value is not None and (not isinstance(value, str) or not value.strip()):
            raise ValueError("Valeur attendue : texte non vide ou null pour une absence.")
    if not isinstance(response, dict) or not isinstance(response.get("document"), dict):
        raise ValueError("Réponse attendue : objet document contenant entities.")
    entities = response["document"].get("entities")
    if not isinstance(entities, list):
        raise ValueError("document.entities doit être une liste, éventuellement vide.")
    values = {}
    issues = []
    for entity in entities:
        if not isinstance(entity, dict) or not isinstance(entity.get("type"), str):
            raise ValueError("Entité invalide : type texte obligatoire.")
        field = entity["type"]
        if entity.get("properties"):
            raise ValueError("Ce contrôleur ne traite que des champs plats.")
        if field not in FIELDS:
            issues.append({"field": field, "reason": "champ_inattendu"})
            continue
        if field in values:
            issues.append({"field": field, "reason": "champ_repete"})
            continue
        value = entity.get("mentionText")
        if not isinstance(value, str) or not value.strip():
            raise ValueError("mentionText non vide obligatoire pour " + field)
        values[field] = value
    for field in sorted(FIELDS):
        wanted = expected[field]
        if wanted is None:
            if field in values:
                issues.append({"field": field, "reason": "valeur_sur_champ_absent"})
        elif field not in values:
            issues.append({"field": field, "reason": "champ_manquant"})
        elif comparable(values[field]) != comparable(wanted):
            issues.append({"field": field, "reason": "valeur_differente"})
    return {"conforme_au_referentiel": not issues, "ecarts": issues}


def main():
    if len(sys.argv) != 3:
        print("Usage : python3 verifier_extraction.py attendu.json reponse.json", file=sys.stderr)
        return 2
    try:
        report = evaluate(load_json(sys.argv[1]), load_json(sys.argv[2]))
    except (OSError, ValueError) as error:
        print(json.dumps({"entree_invalide": str(error)}, ensure_ascii=False))
        return 2
    print(json.dumps(report, ensure_ascii=False, indent=2))
    return 0 if report["conforme_au_referentiel"] else 1


if __name__ == "__main__":
    sys.exit(main())
