#!/usr/bin/env python3
"""Summarize mn103s_emu trace recorder CSV files."""
from __future__ import annotations

import argparse
import csv
import json
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any


def parse_int(value: str) -> int | None:
    if not value:
        return None
    try:
        return int(value, 0)
    except Exception:
        return None


def analyze(path: Path, sample_limit: int = 12) -> dict[str, Any]:
    with path.open("r", newline="", encoding="utf-8", errors="replace") as f:
        rows = list(csv.DictReader(f))
    events = Counter(row.get("event", "") for row in rows)
    names = Counter(row.get("name", "") for row in rows)
    pcs = Counter(row.get("pc", "") for row in rows)
    cycles = [parse_int(row.get("cycle", "")) for row in rows]
    cycles = [c for c in cycles if c is not None]
    watch_rows = [r for r in rows if str(r.get("event", "")).startswith("watch")]
    watch_by_name = Counter(row.get("name", "") for row in watch_rows)
    watch_addr_range: dict[str, dict[str, str]] = {}
    addr_nums_by_name: dict[str, list[int]] = defaultdict(list)
    for row in watch_rows:
        name = row.get("name", "")
        n = parse_int(row.get("addr", ""))
        if name and n is not None:
            addr_nums_by_name[name].append(n)
    for name, vals in addr_nums_by_name.items():
        watch_addr_range[name] = {"first": f"0x{min(vals):08X}", "last": f"0x{max(vals):08X}", "count": str(len(vals))}

    return {
        "path": str(path),
        "rows": len(rows),
        "cycle_min": min(cycles) if cycles else None,
        "cycle_max": max(cycles) if cycles else None,
        "events": dict(events),
        "top_names": names.most_common(sample_limit),
        "top_pc": pcs.most_common(sample_limit),
        "watch_events": len(watch_rows),
        "watch_by_name": dict(watch_by_name),
        "watch_addr_range": watch_addr_range,
        "first_rows": rows[: min(sample_limit, len(rows))],
        "last_rows": rows[-min(sample_limit, len(rows)):] if rows else [],
    }


def print_summary(summary: dict[str, Any]) -> None:
    print(f"[TRACE] {summary['path']}")
    print(f"[TRACE] rows={summary['rows']} cycles={summary['cycle_min']}..{summary['cycle_max']} events={summary['events']}")
    if summary["top_pc"]:
        print("[TRACE] top_pc=" + ", ".join(f"{pc}:{count}" for pc, count in summary["top_pc"] if pc))
    if summary["top_names"]:
        print("[TRACE] top_names=" + ", ".join(f"{name}:{count}" for name, count in summary["top_names"] if name))
    if summary["watch_events"]:
        print(f"[TRACE] watch_events={summary['watch_events']} watch_by_name={summary['watch_by_name']}")
        for name, rng in summary["watch_addr_range"].items():
            print(f"[TRACE] watch_range {name}: {rng['first']}..{rng['last']} ({rng['count']} event(s))")


def main() -> int:
    ap = argparse.ArgumentParser(description="Summarize mn103s_emu trace CSV output")
    ap.add_argument("csv", nargs="+", help="trace CSV file(s)")
    ap.add_argument("--json-out", help="write full summary JSON")
    ap.add_argument("--sample-limit", type=int, default=12)
    args = ap.parse_args()
    summaries = []
    for raw in args.csv:
        path = Path(raw)
        if not path.exists():
            print(f"[-] missing CSV: {path}")
            return 2
        summary = analyze(path, args.sample_limit)
        summaries.append(summary)
        print_summary(summary)
    if args.json_out:
        out = Path(args.json_out)
        payload: Any = summaries[0] if len(summaries) == 1 else summaries
        out.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
        print(f"[OK] wrote {out}")
    return 0


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