"""Two offline demonstrations on synthetic inputs; no AI, network or business API calls."""
import argparse
import csv
from datetime import date
from decimal import Decimal
import hashlib
import json
from pathlib import Path
import platform


def read_csv(path):
    with path.open(encoding="utf-8", newline="") as source:
        return list(csv.DictReader(source))


def write_csv(path, rows):
    with path.open("w", encoding="utf-8", newline="") as output:
        writer = csv.DictWriter(output, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)


def reconciliation(rows, export):
    seen = {}
    accepted = {}
    journal = []
    raw = sum(int(row["amount_kopeks"]) for row in rows)
    for row in rows:
        identity = row["document_id"]
        signature = (row["date"], row["status"], row["amount_kopeks"])
        if identity in seen:
            if seen[identity] != signature:
                raise ValueError("Conflicting duplicate: " + identity)
            reason = "duplicate"
        elif row["status"] != "posted":
            reason = "status_" + row["status"]
        elif not date(2026, 9, 1) <= date.fromisoformat(row["date"]) < date(2026, 10, 1):
            reason = "outside_period"
        else:
            reason = "included"
            accepted[identity] = int(row["amount_kopeks"])
        seen[identity] = signature
        journal.append({**row, "decision": reason})
    comparison = {}
    for row in export:
        if row["document_id"] in comparison:
            raise ValueError("Duplicate export identity")
        comparison[row["document_id"]] = int(row["amount_kopeks"])
    differences = []
    for identity in sorted(accepted.keys() | comparison.keys()):
        left, right = accepted.get(identity), comparison.get(identity)
        if left != right:
            differences.append({"document_id": identity, "ledger_kopeks": left,
                                "export_kopeks": right,
                                "difference_kopeks": None if left is None or right is None else right-left})
    return {"raw_sum_kopeks": raw, "accepted_sum_kopeks": sum(accepted.values()),
            "export_sum_kopeks": sum(comparison.values()), "included_rows": len(accepted),
            "input_rows": len(rows), "differences": differences}, journal


def normalize(value, kind):
    if not value.strip():
        return None
    if kind == "amount":
        number = Decimal("".join(value.split()).replace(",", ".")) * 100
        if number != number.to_integral_value():
            raise ValueError("Amount has fractions smaller than one kopek")
        return int(number)
    if kind == "date":
        return date.fromisoformat(value).isoformat()
    if kind != "text":
        raise ValueError("Unknown kind: " + kind)
    return " ".join(value.split())


def compare_specification(rows):
    results = []
    fields = set()
    for row in rows:
        if row["field"] in fields:
            raise ValueError("Duplicate field")
        fields.add(row["field"])
        before, after = normalize(row["before"], row["kind"]), normalize(row["after"], row["kind"])
        status = "missing_after" if after is None and before is not None else "changed" if before != after else "equal"
        results.append({**row, "before_normalized": before, "after_normalized": after, "decision": status})
    return results


def run(directory, output):
    ledger, journal = reconciliation(read_csv(directory / "ledger.csv"), read_csv(directory / "management-export.csv"))
    spec = compare_specification(read_csv(directory / "specification.csv"))
    assert ledger["raw_sum_kopeks"] == 77000000
    assert ledger["accepted_sum_kopeks"] == 35000000
    assert ledger["export_sum_kopeks"] == 36000000
    assert ledger["differences"] == [{"document_id": "A006", "ledger_kopeks": 7000000, "export_kopeks": 8000000, "difference_kopeks": 1000000}]
    assert {r["field"]: r["decision"] for r in spec} == {
        "specification_id": "equal", "amount": "changed", "currency": "equal",
        "deadline": "changed", "scope": "missing_after", "support": "equal"}
    # Negative controls: duplicates with different data must stop the calculation.
    broken = read_csv(directory / "ledger.csv")
    broken[2]["amount_kopeks"] = "1"
    try:
        reconciliation(broken, read_csv(directory / "management-export.csv"))
    except ValueError:
        pass
    else:
        raise AssertionError("Conflicting duplicate did not stop the calculation")
    output.mkdir(parents=True, exist_ok=True)
    source_names = ["run.py", "ledger.csv", "management-export.csv", "specification.csv"]
    result = {"experiment_version": "offline-control-v1", "data": "synthetic",
              "python": platform.python_version(), "network_calls": 0, "model_calls": 0,
              "sources_sha256": {name: hashlib.sha256((directory / name).read_bytes()).hexdigest() for name in source_names},
              "reconciliation": ledger, "specification": {
                  "fields": len(spec), "literal_differences": sum(r["before"] != r["after"] for r in spec),
                  "normalized_differences": sum(r["decision"] != "equal" for r in spec),
                  "missing_fields": sum(r["decision"] == "missing_after" for r in spec)},
              "negative_control_conflicting_duplicate": "stopped", "expected_results": "passed"}
    write_csv(output / "ledger-decisions.csv", journal)
    write_csv(output / "specification-decisions.csv", spec)
    (output / "result.json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    return result


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output", type=Path, default=Path(__file__).resolve().parent / "output")
    args = parser.parse_args()
    print(json.dumps(run(Path(__file__).resolve().parent, args.output), ensure_ascii=False, indent=2))
