#!/usr/bin/env python3
"""Reproduce a synthetic base-rate exercise. No AI model or detector is run."""

import argparse
import csv
import hashlib
import json
from pathlib import Path


FIELDS = ["scenario", "case_id", "ground_truth", "alerted"]
COHORTS = [("prevalence_10_percent", 100), ("prevalence_1_percent", 10)]


def generate(path):
    """Assign labels and alerts by construction, with no sampling or inference."""
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=FIELDS, lineterminator="\n")
        writer.writeheader()
        for scenario, positives in COHORTS:
            true_positives = positives * 8 // 10
            false_positives = (1000 - positives) // 10
            for index in range(1000):
                truth = index < positives
                alert = index < true_positives if truth else index - positives < false_positives
                writer.writerow({
                    "scenario": scenario,
                    "case_id": f"{scenario}_{index + 1:04d}",
                    "ground_truth": int(truth),
                    "alerted": int(alert),
                })


def divide(numerator, denominator):
    return numerator / denominator if denominator else None


def evaluate(path):
    results = {}
    seen = set()
    with path.open(encoding="utf-8", newline="") as handle:
        reader = csv.DictReader(handle)
        if reader.fieldnames != FIELDS:
            raise ValueError(f"Expected CSV columns: {FIELDS}")
        for row in reader:
            if not row["case_id"] or row["case_id"] in seen:
                raise ValueError("Case IDs must be nonempty and unique")
            seen.add(row["case_id"])
            if row["ground_truth"] not in {"0", "1"} or row["alerted"] not in {"0", "1"}:
                raise ValueError("Labels must be 0 or 1")
            if not row["scenario"]:
                raise ValueError("Scenario must be nonempty")
            counts = results.setdefault(row["scenario"], {"tp": 0, "fp": 0, "fn": 0, "tn": 0})
            bucket = {("1", "1"): "tp", ("0", "1"): "fp", ("1", "0"): "fn", ("0", "0"): "tn"}[
                (row["ground_truth"], row["alerted"])
            ]
            counts[bucket] += 1
    if not results:
        raise ValueError("Dataset is empty")
    for counts in results.values():
        total = sum(counts.values())
        counts.update({
            "cases": total,
            "ground_truth_positives": counts["tp"] + counts["fn"],
            "alerts": counts["tp"] + counts["fp"],
            "prevalence": divide(counts["tp"] + counts["fn"], total),
            "precision": divide(counts["tp"], counts["tp"] + counts["fp"]),
            "recall": divide(counts["tp"], counts["tp"] + counts["fn"]),
            "false_positive_rate": divide(counts["fp"], counts["fp"] + counts["tn"]),
        })
    return {
        "exercise": "Synthetic alert base-rate arithmetic",
        "kind": "educational_synthetic_example",
        "model_or_detector_evaluated": None,
        "real_transactions_used": False,
        "dataset": path.name,
        "dataset_sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
        "total_cases": len(seen),
        "scenarios": results,
        "limitations": "Labels and alerts were assigned to yield 80% recall and 10% false-positive rate. These are assumptions, not measured AI performance. The arithmetic has no empirical confidence interval.",
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--dataset", type=Path, default=Path(__file__).with_name("synthetic-alerts.csv"))
    parser.add_argument("--generate", action="store_true", help="Regenerate the fixed synthetic CSV before evaluating it")
    args = parser.parse_args()
    if args.generate:
        generate(args.dataset)
    print(json.dumps(evaluate(args.dataset), indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()
