#!/usr/bin/env python3
"""Verify emitted READ response files from a media-window-patched session config.

This helper is intentionally tolerant of the existing artifact layouts. It can
read a host/iscsi response JSONL with data_file entries, or it can search a
response directory for files whose names contain the command tag.
"""
from __future__ import annotations

import argparse
import json
import re
import hashlib
from pathlib import Path
from typing import Any

from hlds_gdr_patch_session_media_window import load_json_or_jsonc, parse_hex_bytes, decode_read, find_session_model, parse_int


def tag_to_glob_fragment(tag: str) -> str:
    return re.sub(r"[^A-Za-z0-9]+", "*", tag).strip("*")


def sha256_hex(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def load_jsonl(path: Path) -> list[dict[str, Any]]:
    out = []
    if not path:
        return out
    for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
        line = line.strip()
        if not line:
            continue
        try:
            obj = json.loads(line)
        except Exception:
            continue
        if isinstance(obj, dict):
            out.append(obj)
    return out


def find_response_file(tag: str, index: int, response_dir: Path | None, jsonl_items: list[dict[str, Any]], jsonl_base: Path | None) -> Path | None:
    for item in jsonl_items:
        if str(item.get("tag")) == tag or int(item.get("index", -999999)) == index:
            df = item.get("data_file") or item.get("file") or item.get("path")
            if df:
                p = Path(str(df))
                if not p.is_absolute() and jsonl_base is not None:
                    p = jsonl_base / p
                if p.exists():
                    return p
    if response_dir and response_dir.exists():
        frag = tag_to_glob_fragment(tag)
        cands = sorted(response_dir.glob(f"*{frag}*.bin"))
        if cands:
            return cands[0]
        cands = sorted(response_dir.glob(f"{index:03d}_*.bin"))
        if cands:
            return cands[0]
    return None


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--config", required=True, help="v194 media-window-patched config")
    ap.add_argument("--response-dir", help="Directory containing emitted .bin responses")
    ap.add_argument("--responses-jsonl", help="Optional JSONL with tag/index/data_file records")
    ap.add_argument("--json", dest="json_out", default=None)
    ap.add_argument("--md", dest="md_out", default=None)
    args = ap.parse_args()

    cfg = load_json_or_jsonc(Path(args.config))
    model, model_path = find_session_model(cfg)
    commands = model.get("commands") or []
    response_dir = Path(args.response_dir) if args.response_dir else None
    jsonl_path = Path(args.responses_jsonl) if args.responses_jsonl else None
    jsonl_items = load_jsonl(jsonl_path) if jsonl_path else []
    jsonl_base = jsonl_path.parent if jsonl_path else None

    rows = []
    for idx, cmd in enumerate(commands):
        if not isinstance(cmd, dict):
            continue
        cdb = parse_hex_bytes(cmd.get("cdb_hex"))
        r = decode_read(cdb)
        if not r:
            continue
        tag = str(cmd.get("tag", f"cmd{idx:03d}"))
        expected = parse_hex_bytes(cmd.get("data_in_hex"))
        p = find_response_file(tag, idx, response_dir, jsonl_items, jsonl_base)
        if not p:
            rows.append({"index": idx, "tag": tag, "ok": False, "reason": "response_file_not_found"})
            continue
        got = p.read_bytes()
        ok = got == expected
        rows.append({
            "index": idx,
            "tag": tag,
            "ok": ok,
            "reason": "OK" if ok else "DATA_MISMATCH",
            "response_file": str(p),
            "host_lba": r["host_lba"],
            "blocks": r["blocks"],
            "expected_len": len(expected),
            "actual_len": len(got),
            "expected_sha256": sha256_hex(expected),
            "actual_sha256": sha256_hex(got),
        })

    report = {"ok": all(r.get("ok") for r in rows) if rows else False, "checked": len(rows), "failed": sum(0 if r.get("ok") else 1 for r in rows), "rows": rows}
    if args.json_out:
        Path(args.json_out).write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
    if args.md_out:
        lines = ["# GDR session media response verification", "", f"- ok: `{report['ok']}`", f"- checked: `{report['checked']}`", f"- failed: `{report['failed']}`", "", "| index | tag | ok | reason | bytes | file |", "|---:|---|---|---|---:|---|"]
        for r in rows:
            lines.append(f"| {r.get('index')} | `{r.get('tag')}` | `{r.get('ok')}` | `{r.get('reason')}` | {r.get('actual_len',0)} | `{r.get('response_file','')}` |")
        Path(args.md_out).write_text("\n".join(lines) + "\n", encoding="utf-8")
    print(f"[SESSION-RESPONSES] checked={report['checked']} failed={report['failed']} all={report['ok']}")
    return 0 if report["ok"] else 2


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