#!/usr/bin/env python3
"""Wertet die beiden Batch-Laeufe ueber den neuen Upload aus und stellt sie dem
374er-Referenzkorpus gegenueber.

Quellen:
  batch-results.json           -- Referenzkorpus (374, Lauf vom 15.07.)
  batch2-results-native.json   -- neuer Upload, direkt durch die Pipeline
  batch2-results-converted.json-- neuer Upload, Altformat nach Konvertierung

Vertraulichkeitsdisziplin: gibt Zaehler und Familien aus, keine Zellwerte.
Dateinamen erscheinen nur dort, wo sie fuer die Nacharbeit noetig sind
(unbekannte Familien) -- und dann gekuerzt.
"""
import json
import sys
from collections import Counter
from pathlib import Path

BASE = Path("/home/aria/work/qaf-corpus")


def load(name):
    p = BASE / name
    if not p.exists():
        return None
    with p.open() as fh:
        return json.load(fh)["results"]


ref = load("batch-results.json")
native = load("batch2-results-native.json")
conv = load("batch2-results-converted.json")

if native is None:
    sys.exit("batch2-results-native.json fehlt - Lauf A noch nicht fertig?")

# Konvertierte Ergebnisse ueberschreiben die "legacy_xls_unsupported"-Zeilen des
# nativen Laufs: gleiche Datei, besseres Ergebnis. Schluessel ist der Dateiname
# ohne Endung, weil die Konvertierung .xls -> .xlsx umbenennt.
def stem(n):
    return n.rsplit(".", 1)[0]


merged = {stem(r["file"]): r for r in native}
replaced = 0
rescued_empty = 0
if conv:
    for r in conv:
        k = stem(r["file"])
        if k not in merged:
            merged[k] = {**r, "_via": "konvertiert"}
            continue
        cur = merged[k]
        # Ersetzen, wenn der native Lauf gar nicht laden konnte ODER "geladen"
        # meldet, aber 0 Blätter liefert (ExcelJS wirft bei manchen Altdateien
        # keinen Fehler, gibt aber eine leere Mappe zurück — stiller Datenverlust,
        # der sonst als "unknown Profil" statt als Formatproblem gezählt wird).
        native_empty = cur.get("loadOk") and not cur.get("sheetCount")
        if r.get("loadOk") and r.get("sheetCount"):
            if not cur.get("loadOk"):
                merged[k] = {**r, "_via": "konvertiert"}
                replaced += 1
            elif native_empty:
                merged[k] = {**r, "_via": "konvertiert (nativ leer)"}
                rescued_empty += 1

rows = list(merged.values())

print(f"=== Neuer Upload: {len(rows)} Dateien ===")
if conv:
    print(f"davon durch Konvertierung erst lesbar gemacht: {replaced}")
    print(f"davon nativ 'geladen' aber leer, per Konvertierung gerettet: {rescued_empty}")
empty_left = [r for r in rows if r.get("loadOk") and not r.get("sheetCount")]
print(f"weiterhin geladen-aber-leer (echter Formatverlust): {len(empty_left)}")
print()

# --- Ladequote ---
ok = [r for r in rows if r.get("loadOk")]
print(f"geladen: {len(ok)}/{len(rows)} ({round(len(ok)/len(rows)*100)}%)")
err_classes = Counter(r.get("loadErrorClass") for r in rows if not r.get("loadOk"))
for k, v in err_classes.most_common():
    print(f"   Ladefehler {k}: {v}")
print()

# --- Primaere Klassifikation ---
print("--- Klassifikation ---")
cls = Counter(r.get("primaryClassification") for r in rows)
for k, v in cls.most_common():
    print(f"  {str(k):32} {v:5}  {round(v/len(rows)*100,1)}%")
print()

# --- Standard-Template-Familien ---
print("--- Standard-Template-Profile ---")
fam = Counter()
klass = Counter()
for r in rows:
    fp = r.get("standardTemplateFingerprint") or {}
    if fp:
        fam[fp.get("matchedProfile") or "(kein Profil / unknown)"] += 1
        klass[fp.get("classification")] += 1
for k, v in fam.most_common(12):
    print(f"  {str(k):32} {v:5}")
print("  Klassifikation:", dict(klass))
print()

# --- Multi-QAF ---
print("--- Multi-QAF-Familien ---")
mq = Counter()
mqcls = Counter()
for r in rows:
    m = r.get("multiQaf") or {}
    mqcls[m.get("classification")] += 1
    if r.get("multiQafTemplateFamily"):
        mq[r["multiQafTemplateFamily"]] += 1
print("  Detektion:", dict(mqcls))
for k, v in mq.most_common():
    print(f"  {str(k):32} {v:5}")
print()

# --- G60 ---
g60 = [r for r in rows if r.get("g60Detected")]
print(f"--- G60-Detail erkannt: {len(g60)} ---")
if g60:
    print("  Familien:", dict(Counter(r.get("g60FingerprintFamily") for r in g60)))
print()

# --- Fehler waehrend des Parsens ---
print("--- Parse-Fehlerklassen (Modul-Ebene) ---")
perr = Counter()
for r in rows:
    for e in r.get("errors") or []:
        kind = e.split(":")[0] if isinstance(e, str) else str(e.get("stage", "?"))
        perr[kind] += 1
for k, v in perr.most_common(10):
    print(f"  {k:40} {v:5}")
print()

# --- Abgleich gegen Referenzkorpus ---
if ref:
    ref_hashes = {r.get("sha256_16") for r in ref if r.get("sha256_16")}
    new_hashes = [r.get("sha256_16") for r in rows if r.get("sha256_16")]
    dupes = sum(1 for h in new_hashes if h in ref_hashes)
    internal = len(new_hashes) - len(set(new_hashes))
    print("--- Abgleich mit dem 374er-Referenzkorpus ---")
    print(f"  identisch zu einer Referenzdatei: {dupes}")
    print(f"  Duplikate innerhalb des Uploads:  {internal}")
    print(f"  echte Neuzugaenge:                {len(set(new_hashes)) - dupes}")
    print()

    ref_fam = Counter()
    for r in ref:
        fp = r.get("standardTemplateFingerprint") or {}
        if fp:
            ref_fam[fp.get("matchedProfile") or "(kein Profil / unknown)"] += 1
    print("  Familien-Verteilung im Vergleich (Referenz -> neu):")
    for k in sorted(set(ref_fam) | set(fam), key=lambda x: -(fam.get(x, 0))):
        print(f"    {str(k):32} {ref_fam.get(k,0):5} -> {fam.get(k,0):5}")
    print()

# --- Unbekannte: die eigentliche Ausbeute ---
unknown = [
    r for r in rows
    if r.get("loadOk")
    and (r.get("standardTemplateFingerprint") or {}).get("classification") == "unknown"
]
print(f"--- Ohne Profil-Match (Kandidaten fuer neue Familien): {len(unknown)} ---")
for r in unknown[:25]:
    print(f"    {r['file'][:70]}  sheets={r.get('sheetCount')}")
if len(unknown) > 25:
    print(f"    ... und {len(unknown)-25} weitere")
