#!/usr/bin/env python3
"""Run the exact MJ-RC5 three-capture regression against pinned OMRChecker."""

from __future__ import annotations

import argparse
import csv
import json
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
PROFILE = ROOT / "profiles" / "mj-rc5"
FIXTURES = ROOT / "fixtures" / "mj-rc5"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument("--omrchecker", required=True, type=Path)
    parser.add_argument("--keep-output", type=Path)
    return parser.parse_args()


def locate_results(output: Path) -> Path:
    candidates = list((output / "Manual").rglob("MultiMarkedFiles.csv"))
    if len(candidates) != 1:
        raise RuntimeError(f"Expected one MultiMarkedFiles.csv, found {candidates}")
    return candidates[0]


def combined(row: dict[str, str], prefix: str, count: int) -> str:
    return "".join(row[f"{prefix}_{index}"] for index in range(1, count + 1))


def main() -> int:
    args = parse_args()
    omr_main = args.omrchecker.resolve() / "main.py"
    if not omr_main.is_file():
        raise FileNotFoundError(omr_main)

    expected = json.loads((FIXTURES / "expected_results.json").read_text())["fixtures"]
    temporary = None
    if args.keep_output:
        work = args.keep_output.resolve()
        if work.exists():
            raise FileExistsError(f"Refusing to replace existing directory: {work}")
        work.mkdir(parents=True)
    else:
        temporary = tempfile.TemporaryDirectory(prefix="mj-rc5-regression-")
        work = Path(temporary.name)

    input_dir = work / "input"
    output_dir = work / "output"
    input_dir.mkdir()
    output_dir.mkdir()
    for name in ("template.json", "config.json", "omr_marker.jpg"):
        shutil.copy2(PROFILE / name, input_dir / name)
    for name in expected:
        source_name = "Epson_MJ_RC5.pdf" if name == "Epson_MJ_RC5.png" else name
        shutil.copy2(FIXTURES / source_name, input_dir / source_name)

    completed = subprocess.run(
        [sys.executable, str(omr_main), "-i", str(input_dir), "-o", str(output_dir)],
        cwd=args.omrchecker.resolve(),
        text=True,
        capture_output=True,
    )
    if completed.returncode:
        print(completed.stdout)
        print(completed.stderr, file=sys.stderr)
        return completed.returncode

    with locate_results(output_dir).open(newline="") as handle:
        rows = {row["file_id"]: row for row in csv.DictReader(handle)}

    failures: list[str] = []
    for file_id, wanted in expected.items():
        row = rows.get(file_id)
        if row is None:
            failures.append(f"{file_id}: not routed to manual review")
            continue
        actual = {
            "round": row["round_tens"] + row["round_ones"],
            "player_id": combined(row, "player_id", 3),
            "control_left": row["control_left"],
            "control_right": row["control_right"],
            "game_1_raw": row["game_1"],
            "game_2": row["game_2"],
            "game_3": row["game_3"],
            "game_4": row["game_4"],
        }
        for key, value in actual.items():
            if value != wanted[key]:
                failures.append(f"{file_id}: {key} expected {wanted[key]!r}, got {value!r}")

    if set(rows) != set(expected):
        failures.append(f"Unexpected manual-review files: {sorted(set(rows) - set(expected))}")
    if failures:
        print("FAIL: MJ-RC5 regression")
        for failure in failures:
            print(f"- {failure}")
        return 1
    print("PASS: MJ-RC5 Epson and two Pixel fixtures returned exact values and manual review")
    if args.keep_output:
        print(f"Output retained at {work}")
    if temporary:
        temporary.cleanup()
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
