"""Compare read-tool eval result sets (baseline vs feature labels).

Usage:
  python3 evals/readtool/report.py --labels baseline feat-fifo-guard
  python3 evals/readtool/report.py --labels baseline feat-fifo-guard --model qwen_qwen3.8-max
"""

from __future__ import annotations

import argparse
import json
from collections import defaultdict
from pathlib import Path
from statistics import mean

RESULTS = Path(__file__).resolve().parent / "results"

METRICS = ["score", "api_turns", "tool_calls", "read_file_calls", "total_tokens", "wall_s"]


def load_label(label: str, model_filter: str | None) -> dict:
    """-> {model: {task_id: {metric: [values across reps]}}}"""
    out: dict = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
    root = RESULTS / label
    if not root.is_dir():
        raise SystemExit(f"no results for label '{label}' under {root}")
    for model_dir in sorted(root.iterdir()):
        if model_filter and model_dir.name != model_filter:
            continue
        for rep_file in sorted(model_dir.glob("rep*.json")):
            data = json.loads(rep_file.read_text())
            for rec in data["records"]:
                if rec.get("error"):
                    # count errored task-runs as score 0 but keep them in the
                    # denominator; efficiency metrics excluded (not comparable)
                    out[model_dir.name][rec["task_id"]]["score"].append(0.0)
                    out[model_dir.name][rec["task_id"]]["errors"].append(1)
                    continue
                for metric in METRICS:
                    if metric in rec and rec[metric] is not None:
                        out[model_dir.name][rec["task_id"]][metric].append(rec[metric])
    return out


def fmt(v: float, metric: str) -> str:
    if metric == "score":
        return f"{v:.3f}"
    if metric == "wall_s":
        return f"{v:.0f}s"
    return f"{v:,.0f}"


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--labels", nargs="+", required=True)
    ap.add_argument("--model", default=None, help="model slug filter (dir name)")
    args = ap.parse_args()

    sets = {lbl: load_label(lbl, args.model) for lbl in args.labels}
    models = sorted({m for s in sets.values() for m in s})

    for model in models:
        print(f"\n=== {model} ===")
        task_ids = sorted(
            {t for lbl in args.labels for t in sets[lbl].get(model, {})}
        )
        # Per-task score table
        header = f"{'task':<22}" + "".join(f"{lbl:>24}" for lbl in args.labels)
        print(header)
        print("-" * len(header))
        for tid in task_ids:
            row = f"{tid:<22}"
            for lbl in args.labels:
                vals = sets[lbl].get(model, {}).get(tid, {})
                sc = vals.get("score", [])
                turns = vals.get("api_turns", [])
                tok = vals.get("total_tokens", [])
                cell = (
                    f"{mean(sc):.2f} ({len(sc)}r) "
                    f"t={mean(turns):.1f} " if turns else f"{mean(sc):.2f} ({len(sc)}r) t=? "
                ) if sc else "—"
                if sc and tok:
                    cell += f"tk={mean(tok)/1000:.0f}k"
                row += f"{cell:>24}"
            print(row)
        # Aggregates
        print()
        for metric in METRICS:
            row = f"{'MEAN ' + metric:<22}"
            base_val = None
            for lbl in args.labels:
                per_task = []
                for tid in task_ids:
                    vals = sets[lbl].get(model, {}).get(tid, {}).get(metric, [])
                    if vals:
                        per_task.append(mean(vals))
                if per_task:
                    v = mean(per_task)
                    delta = ""
                    if base_val is not None and base_val != 0:
                        pct = (v - base_val) / base_val * 100
                        delta = f" ({pct:+.0f}%)"
                    if base_val is None:
                        base_val = v
                    row += f"{fmt(v, metric) + delta:>24}"
                else:
                    row += f"{'—':>24}"
            print(row)
    print(
        "\nNote: efficiency means are per-task means over reps, then averaged "
        "across tasks (never sums). Errored runs score 0 but are excluded "
        "from efficiency means."
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
