#!/usr/bin/env python3
"""v121 robust sidecar packet matrix runner.

Avoids CMD CALL :label parsing issues on some Windows/cmd combinations by
running the matrix from Python instead of a batch subroutine.
"""
from __future__ import annotations

import argparse
import subprocess
import sys
from pathlib import Path

WORKSPACE = r"D:\vscode-workspace\xbox-dvd-tools-v2"
FWBIN = r"D:\vscode-workspace\xbox-dvd-tools-v2\firmware\GDR-8050L\8050L_fw_plaintext_256k.bin"

PROBES = [
    ("locked_read_capacity", "GDR-8050L v121 locked READ CAPACITY post-timer", "read-capacity-10", "locked", 90000),
    ("locked_mode_sense_3e", "GDR-8050L v121 locked MODE SENSE 3E post-timer", "mode-sense-auth-xbox", "locked", 90000),
    ("unlocked_read_capacity", "GDR-8050L v121 unlocked READ CAPACITY post-timer", "read-capacity-10", "unlocked", 90000),
    ("unlocked_mode_sense_3e", "GDR-8050L v121 unlocked MODE SENSE 3E post-timer", "mode-sense-auth-xbox", "unlocked", 90000),
    ("read_dvd_structure_ad_c0", "GDR-8050L v121 READ DVD STRUCTURE AD C0 post-timer", "read-dvd-structure-xbox", "locked", 120000),
    ("read_dvd_structure_pfi", "GDR-8050L v121 READ DVD STRUCTURE PFI post-timer", "read-dvd-structure-pfi", "locked", 120000),
    ("read_dvd_structure_dmi", "GDR-8050L v121 READ DVD STRUCTURE DMI post-timer", "read-dvd-structure-dmi", "locked", 120000),
]


def run_checked(cmd: list[str], *, cwd: Path, stdout_file: Path | None = None) -> None:
    if stdout_file is None:
        res = subprocess.run(cmd, cwd=str(cwd))
    else:
        with stdout_file.open("w", encoding="utf-8", errors="replace") as fh:
            res = subprocess.run(cmd, cwd=str(cwd), stdout=fh, stderr=subprocess.STDOUT)
    if res.returncode != 0:
        raise SystemExit(res.returncode)


def run_probe(script_dir: Path, xdvd_json: str, iso: str | None, tag: str, title: str, packet_cdb: str, state: str, max_cycles: int) -> None:
    print(f"\n[V121] Probe: {tag} CDB={packet_cdb} state={state}", flush=True)
    stem = f"gdr8050l_auth_v121_{tag}_post_timer"
    trace_csv = f"{stem}_trace.csv"
    config_json = f"{stem}_config.json"
    output_txt = script_dir / f"{stem}.output.txt"

    run_cmd = [
        sys.executable,
        "run_direct_firmware.py",
        "--workspace", WORKSPACE,
        "--bin", FWBIN,
        "--base", "0x90000000",
        "--entry", "0x90000020",
        "--model", "GDR-8050L",
        "--name", title,
        "--max-cycles", str(max_cycles),
        "--sp", "0x003FF000",
        "--preset", "gdr8050l-packet-full-cdb",
        "--packet-cdb", packet_cdb,
        "--xdvd-sidecar", xdvd_json,
        "--xdvd-state", state,
        "--packet-data-in-source-mode", "stream",
        "--packet-data-in-source-no-prelude",
        "--packet-data-in-model-match-len", "8",
        "--gdr-hw-model",
        "--clear-preset-stops",
        "--stop-pc", "0x90006608",
        "--stop-pc-hit-count", "4",
        "--stop-pc-min-cycle", "51200",
        "--auth-watch",
        "--trace-csv", trace_csv,
        "--trace-summary",
        "--config-out", config_json,
    ]
    if iso:
        run_cmd.extend(["--iso", iso])

    try:
        run_checked(run_cmd, cwd=script_dir, stdout_file=output_txt)
    except SystemExit as e:
        print(f"[FAIL] v121 run failed for {tag}. Send {output_txt.name}", flush=True)
        raise

    run_checked([
        sys.executable,
        "analyze_gdr8050l_v109_readcap_route.py",
        trace_csv,
        "--out", f"{stem}_summary.md",
    ], cwd=script_dir)

    run_checked([
        sys.executable,
        "extract_gdr_handshake.py",
        trace_csv,
        "--out-json", f"{stem}_handshake.json",
        "--config", config_json,
        "--out-df70", f"{stem}_df70_all.bin",
        "--out-df70-after-packet", f"{stem}_df70_after_packet.bin",
    ], cwd=script_dir)

    try:
        run_checked([
            sys.executable,
            "analyze_gdr_chunked_data_in.py",
            trace_csv,
            "--config", config_json,
            "--out", f"{stem}_verify.md",
            "--out-payload", f"{stem}_payload_reconstructed.bin",
        ], cwd=script_dir)
    except SystemExit as e:
        print(f"[FAIL] v121 payload verification failed for {tag}. Send {stem}_verify.md", flush=True)
        raise

    print(f"[OK] {tag} passed.", flush=True)


def main() -> int:
    ap = argparse.ArgumentParser(description="Run GDR-8050L v121 sidecar packet matrix")
    ap.add_argument("xdvd_json", help="Path to .xdvd.json sidecar")
    ap.add_argument("iso", nargs="?", help="Optional ISO path for metadata")
    args = ap.parse_args()

    script_dir = Path(__file__).resolve().parent
    print(f"[V121] XDVD sidecar: {args.xdvd_json}", flush=True)
    if args.iso:
        print(f"[V121] ISO metadata: {args.iso}", flush=True)
    print("[V121] Sidecar packet matrix: locked/unlocked capacity + mode page + AD/PFI/DMI.", flush=True)
    print("[V121] Python runner avoids CMD CALL :label parsing issues.", flush=True)

    for tag, title, packet_cdb, state, max_cycles in PROBES:
        run_probe(script_dir, args.xdvd_json, args.iso, tag, title, packet_cdb, state, max_cycles)

    print("[OK] v121 sidecar packet matrix complete.", flush=True)
    print("[NEXT] Zip/send gdr8050l_auth_v121_*output.txt, *_summary.md, *_handshake.json, *_verify.md, and *_payload_reconstructed.bin.", flush=True)
    return 0


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