from __future__ import annotations

import json
import os
from pathlib import Path

from jarvis_finance.services.household_classification import (
    ambiguous_review_family,
    merchant_family,
)

SOURCE = Path("/home/agent/jarvis_runtime/finance-system/sprint16.3-analysis/sprint16.3-calibrated-preview-private.json")
OUT_JSON = Path("/home/agent/jarvis_runtime/finance-system/sprint16.3-analysis/sprint16.3-calibration-v2.3-private.json")
OUT_MD = Path("/home/agent/jarvis_runtime/finance-system/sprint16.3-analysis/sprint16.3-calibration-v2.3-private.md")


def proposal(
    merchant: str,
    semantics: str,
    existing: str | None = None,
    conflicting_history: bool = False,
) -> dict:
    ambiguous = ambiguous_review_family(merchant)
    family = merchant_family(merchant) if semantics == "expense" and not ambiguous else None
    if family and not conflicting_history:
        return {
            "suggested_existing_category": family["category_name"],
            "reason": f"Öffentliches, allgemeingültiges Händlermerkmal: {family['label']}.",
            "positive_evidence": ["Ausgabenrichtung", "deterministische öffentliche Händlerfamilie"],
            "negative_guardrails": ["kein reiner Zahlungsdienst", "keine allgemeine Fallback-Kategorie", "keine Einnahmenableitung"],
            "possible_misclassification": "Abweichendes Sortiment oder gemischte Waren; einzelne Ausnahmen bleiben möglich.",
            "confidence": "high",
            "deterministic_rule_safe": True,
        }
    reason = "Mehrdeutige Händlerfamilie; eine allgemeingültige Kategorie ist nicht sicher ableitbar."
    guards = ["keine Fallback-Kategorie", "keine Ableitung nur aus dem Vorzeichen"]
    if conflicting_history:
        reason = "Bestätigte Historie enthält mehrere Kategorien; die öffentliche Händlerfamilie darf diesen Konflikt nicht überschreiben."
        guards.extend(["Historienkonflikt bleibt fail-closed", "keine Regel überschreibt bestätigte Gegenbeispiele"])
    elif ambiguous:
        reason = f"Bewusst mehrdeutig: {ambiguous['label']}."
        guards.append("Marktplatz oder Mehrsortimenter kann verschiedene Kategorien enthalten")
    elif semantics == "income":
        reason = "Einnahmeherkunft ist ohne bestätigte fachliche Zuordnung nicht eindeutig."
        guards.extend(["positiver Betrag ist keine Einkommensevidenz", "möglicher Eigenkonto-Transfer bleibt fail-closed"])
    return {
        "suggested_existing_category": existing or None,
        "reason": reason,
        "positive_evidence": [f"Buchungsrichtung: {semantics}"],
        "negative_guardrails": guards,
        "possible_misclassification": "Falsche Kategorie oder fälschlich als Einkommen statt Transfer erfasst.",
        "confidence": "low" if semantics == "income" or ambiguous or conflicting_history else "medium",
        "deterministic_rule_safe": False,
    }


def main() -> None:
    source = json.loads(SOURCE.read_text(encoding="utf-8"))
    item_by_token = {str(item.get("row_token")): item for item in source["items"]}
    clusters = sorted(
        (cluster for cluster in source["merchant_clusters"] if int(cluster["count"]) >= 2),
        key=lambda cluster: (-int(cluster["count"]), str(cluster["merchant_family"])),
    )
    cluster_report = []
    clustered_tokens: set[str] = set()
    for cluster in clusters:
        clustered_tokens.update(str(value) for value in cluster.get("row_tokens", []))
        entry = {
            "merchant_family": cluster["merchant_family"],
            "count": cluster["count"],
            "direction": cluster["transaction_semantics"],
        }
        entry.update(proposal(
            str(cluster["merchant_family"]),
            str(cluster["transaction_semantics"]),
            cluster.get("category_name"),
            any(
                "unterschiedlich kategorisiert" in str(item_by_token.get(str(token), {}).get("explanation", "")).casefold()
                for token in cluster.get("row_tokens", [])
            ),
        ))
        cluster_report.append(entry)

    individual = []
    for item in source["items"]:
        if item.get("user_state") != "decision_needed" or str(item.get("row_token")) in clustered_tokens:
            continue
        entry = {
            "merchant_family": item.get("merchant") or "Nicht eindeutig",
            "count": 1,
            "direction": item.get("transaction_semantics"),
            "date": item.get("date"),
            "amount": item.get("amount"),
            "currency": item.get("currency"),
        }
        entry.update(proposal(
            str(item.get("merchant") or ""),
            str(item.get("transaction_semantics") or ""),
            item.get("category_name"),
            "unterschiedlich kategorisiert" in str(item.get("explanation", "")).casefold(),
        ))
        individual.append(entry)

    report = {
        "source_classification_version": source["classification_version"],
        "source_preview_fingerprint": source["preview_fingerprint"],
        "cluster_count": len(cluster_report),
        "individual_review_count": len(individual),
        "safe_deterministic_cluster_count": sum(bool(item["deterministic_rule_safe"]) for item in cluster_report),
        "clusters": cluster_report,
        "individual_reviews": individual,
    }
    OUT_JSON.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
    lines = [
        "# Sprint 16.3 – private Kalibrierung",
        "",
        f"- Händlercluster: {len(cluster_report)}",
        f"- Einzelprüfungen: {len(individual)}",
        f"- sichere deterministische Cluster: {report['safe_deterministic_cluster_count']}",
        "",
        "## Händlercluster",
    ]
    for item in cluster_report:
        lines.extend([
            f"### {item['merchant_family']} – {item['count']} Buchungen",
            f"- Richtung: {item['direction']}",
            f"- Vorschlag: {item['suggested_existing_category'] or 'Nutzerentscheidung erforderlich'}",
            f"- Begründung: {item['reason']}",
            f"- Positive Evidenz: {', '.join(item['positive_evidence'])}",
            f"- Negative Guardrails: {', '.join(item['negative_guardrails'])}",
            f"- Mögliche Fehlklassifikation: {item['possible_misclassification']}",
            f"- Vertrauen: {item['confidence']}",
            "",
        ])
    lines.append("## Einzelprüfungen")
    for item in individual:
        lines.extend([
            f"- {item['merchant_family']} – {item['date']} · {item['amount']} {item['currency']} · Vorschlag: {item['suggested_existing_category'] or 'Nutzerentscheidung erforderlich'} · Vertrauen: {item['confidence']}",
        ])
    OUT_MD.write_text("\n".join(lines) + "\n", encoding="utf-8")
    os.chmod(OUT_JSON, 0o600)
    os.chmod(OUT_MD, 0o600)
    print(json.dumps({key: report[key] for key in ("cluster_count", "individual_review_count", "safe_deterministic_cluster_count")}, indent=2))


if __name__ == "__main__":
    main()
