#!/usr/bin/env python3
"""Regression test for extract_fields.py against the erp#39 golden set.

The email skill's extraction was a heredoc inside a shell script: impossible to
run in isolation, therefore never measured, therefore silently wrong. This pins
it to the 16 hand-verified supplier invoices in fleet/golden/invoice-extract/.

Scored per field, and a WRONG value counts against us far more than a missing
one: emitting Arcodange's VAT number as an invoice reference is worse than
emitting nothing, because a human copies it without looking.

  python3 test_extract.py            # summary
  python3 test_extract.py --verbose  # per-document diff
"""
from __future__ import annotations

import json
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from extract_fields import extract, parse_amount

REPO = os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", ".."))
GOLDEN = os.path.join(REPO, "fleet", "golden", "invoice-extract")
VERBOSE = "--verbose" in sys.argv


def norm_date(s):
    """dd/mm/yyyy or yyyy-mm-dd → yyyy-mm-dd; anything else stays as-is."""
    if not s:
        return None
    s = s.strip()
    if len(s) == 10 and s[4] == "-":
        return s
    for sep in ("/", ".", "-"):
        p = s.split(sep)
        if len(p) == 3 and all(x.strip().isdigit() for x in p):
            d, m, y = (x.strip() for x in p)
            if len(y) == 2:
                y = "20" + y
            if len(y) == 4:
                return f"{y}-{int(m):02d}-{int(d):02d}"
    return s


def main() -> int:
    inputs = os.path.join(GOLDEN, "inputs")
    if not os.path.isdir(inputs):
        print(f"golden set not found at {GOLDEN} — skipping", file=sys.stderr)
        return 0

    stats = {f: {"ok": 0, "wrong": 0, "missing": 0} for f in ("invoice_ref", "invoice_date_raw", "total_ht", "total_ttc")}
    docs = 0
    for name in sorted(os.listdir(inputs)):
        if not name.endswith(".json"):
            continue
        doc = json.load(open(os.path.join(inputs, name)))
        exp_path = os.path.join(GOLDEN, "expected", name)
        if not os.path.exists(exp_path):
            continue
        exp = json.load(open(exp_path))
        got = extract(doc["text"])
        docs += 1

        checks = {
            "invoice_ref": (exp.get("ref_supplier"), got.get("invoice_ref")),
            "invoice_date_raw": (exp.get("date_issue"), norm_date(got.get("invoice_date_raw"))),
            "total_ht": (exp.get("totals", {}).get("ht"), parse_amount(got.get("total_ht") or "")),
            "total_ttc": (exp.get("totals", {}).get("ttc"), parse_amount(got.get("total_ttc") or "")),
        }
        for field, (want, have) in checks.items():
            if have is None:
                stats[field]["missing"] += 1
                verdict = "MANQUE"
            elif isinstance(want, (int, float)) and isinstance(have, (int, float)):
                ok = abs(float(want) - float(have)) <= 0.01
                stats[field]["ok" if ok else "wrong"] += 1
                verdict = "ok" if ok else "FAUX"
            else:
                ok = str(want) == str(have)
                stats[field]["ok" if ok else "wrong"] += 1
                verdict = "ok" if ok else "FAUX"
            if VERBOSE and verdict != "ok":
                print(f"  {name[:34]:<36}{field:<18}{verdict:<8}attendu={want!r} obtenu={have!r}")

    print(f"\n{docs} documents du golden set\n")
    print(f"{'champ':<20}{'exact':>7}{'FAUX':>7}{'absent':>9}   {'exactitude'}")
    print("-" * 62)
    total_wrong = 0
    for f, s in stats.items():
        total = s["ok"] + s["wrong"] + s["missing"]
        total_wrong += s["wrong"]
        print(f"{f:<20}{s['ok']:>7}{s['wrong']:>7}{s['missing']:>9}   {100*s['ok']/total:.1f} %")
    print("-" * 62)
    print(f"\nvaleurs FAUSSES (le pire cas — un humain les recopie) : {total_wrong}")
    return 1 if total_wrong else 0


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