#!/usr/bin/env python3
"""Summarize branch gates on save-selector frontier edges."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"


def byte_at_word(value_hex: str, index: int) -> int:
    value = int(value_hex, 16)
    return (value >> (index * 8)) & 0xFF


def accepted_render_by_map(render_reviews: dict) -> dict[str, dict]:
    result = {}
    for review in render_reviews.values():
        if review.get("state") == "accepted":
            result[review["map"]] = review
    return result


def record_tilesets_for(row: dict, map_name: str) -> list[str]:
    for pair in row.get("scenePairs") or []:
        if row.get("source") == map_name:
            return pair.get("sourceTilesets") or []
        if row.get("target") == map_name:
            return pair.get("targetTilesets") or []
    return []


def branch_target_kind(leaf: dict, va_hex: str | None) -> str:
    if not va_hex:
        return "-"
    for word in leaf.get("words") or []:
        if word.get("valueHex") == va_hex:
            if word.get("cns"):
                return f"cns:{word['cns']}"
            if word.get("pointer"):
                return "pointer"
    return "unknown"


def build_rows(
    frontier: list[dict],
    stream_traces: list[dict],
    leaf_streams: list[dict],
    render_reviews: dict,
) -> list[dict]:
    traces_by_stream = {trace.get("streamVaHex"): trace for trace in stream_traces}
    leaves_by_pointer = {leaf.get("leafPointerHex"): leaf for leaf in leaf_streams}
    accepted_render = accepted_render_by_map(render_reviews)
    rows = []
    for edge in frontier:
        source_render = accepted_render.get(edge["source"]) or {}
        target_render = accepted_render.get(edge["target"]) or {}
        source_record_tilesets = record_tilesets_for(edge, edge["source"])
        target_record_tilesets = record_tilesets_for(edge, edge["target"])
        branch_steps = []
        for leaf_pointer in edge.get("leafPointers") or []:
            trace = traces_by_stream.get(leaf_pointer)
            leaf = leaves_by_pointer.get(leaf_pointer) or {}
            for step in (trace or {}).get("trace") or []:
                if step.get("opcodeHex") != "0x11":
                    continue
                value_hex = step["valueHex"]
                table = "primaryBranchState" if byte_at_word(value_hex, 1) == 0 else "secondaryBranchState"
                selector_offset = byte_at_word(value_hex, 2)
                branch_steps.append({
                    "leafPointerHex": leaf_pointer,
                    "streamVaHex": step.get("vaHex"),
                    "valueHex": value_hex,
                    "stateTable": table,
                    "selectionBufferOffset": selector_offset,
                    "selectionBufferOffsetHex": f"0x{selector_offset:02x}",
                    "condition": f"{table}[selectionBuffer[0x{selector_offset:02x}]] == 1",
                    "fallthroughVaHex": step.get("fallthroughVaHex"),
                    "branchTargetHex": step.get("branchTargetHex"),
                    "branchTargetKind": branch_target_kind(leaf, step.get("branchTargetHex")),
                })
        source_tileset_match = source_record_tilesets[:2] == (source_render.get("tilesets") or [])[:2]
        target_tileset_match = target_record_tilesets[:2] == (target_render.get("tilesets") or [])[:2]
        rows.append({
            "source": edge["source"],
            "target": edge["target"],
            "leafPointers": edge.get("leafPointers") or [],
            "branchSteps": branch_steps,
            "sourceRecordTilesets": source_record_tilesets,
            "targetRecordTilesets": target_record_tilesets,
            "sourceAcceptedRender": {
                "visual": source_render.get("visual"),
                "tilesets": source_render.get("tilesets") or [],
                "tileIndexMode": source_render.get("tileIndexMode") or "row40_zero",
            },
            "targetAcceptedRender": {
                "visual": target_render.get("visual"),
                "tilesets": target_render.get("tilesets") or [],
                "tileIndexMode": target_render.get("tileIndexMode") or "row40_zero",
            },
            "sourceTilesetMatch": source_tileset_match,
            "targetTilesetMatch": target_tileset_match,
            "promotionRisk": (
                "high"
                if not branch_steps or not source_tileset_match or not target_tileset_match
                else "medium"
            ),
            "note": (
                "Save-selector scene records are gated script resource bundles, not confirmed tile hotspots. "
                "Do not promote this edge to normal gameplay until a source coordinate/spawn is verified."
            ),
        })
    return rows


def markdown(rows: list[dict]) -> str:
    lines = [
        "# Save Selector Frontier Branches",
        "",
        "Branch gates and render-record mismatches for save-selector frontier edges.",
        "",
        "| source | target | risk | branch gates | record tilesets | accepted render | note |",
        "| --- | --- | --- | --- | --- | --- | --- |",
    ]
    for row in rows:
        gates = "<br>".join(
            f"`{step['leafPointerHex']}` `{step['condition']}` fallthrough `{step.get('fallthroughVaHex')}` target `{step.get('branchTargetHex')}` ({step['branchTargetKind']})"
            for step in row["branchSteps"]
        ) or "-"
        record_tilesets = (
            f"{row['source']}: `{','.join(row['sourceRecordTilesets']) or '-'}`<br>"
            f"{row['target']}: `{','.join(row['targetRecordTilesets']) or '-'}`"
        )
        accepted = (
            f"{row['source']}: `{','.join(row['sourceAcceptedRender']['tilesets']) or '-'}` "
            f"{row['sourceAcceptedRender']['visual'] or '-'}<br>"
            f"{row['target']}: `{','.join(row['targetAcceptedRender']['tilesets']) or '-'}` "
            f"{row['targetAcceptedRender']['visual'] or '-'}"
        )
        lines.append(
            f"| {row['source']} | {row['target']} | {row['promotionRisk']} | {gates} | "
            f"{record_tilesets} | {accepted} | {row['note']} |"
        )
    if not rows:
        lines.append("| - | - | - | - | - | - | - |")
    return "\n".join(lines) + "\n"


def html_page(rows: list[dict]) -> str:
    body = []
    for row in rows:
        gates = "<br>".join(
            html.escape(
                f"{step['leafPointerHex']} {step['condition']} fallthrough {step.get('fallthroughVaHex')} "
                f"target {step.get('branchTargetHex')} ({step['branchTargetKind']})"
            )
            for step in row["branchSteps"]
        ) or "-"
        record_tilesets = (
            f"{html.escape(row['source'])}: <code>{html.escape(','.join(row['sourceRecordTilesets']) or '-')}</code><br>"
            f"{html.escape(row['target'])}: <code>{html.escape(','.join(row['targetRecordTilesets']) or '-')}</code>"
        )
        accepted = (
            f"{html.escape(row['source'])}: <code>{html.escape(','.join(row['sourceAcceptedRender']['tilesets']) or '-')}</code> "
            f"{html.escape(row['sourceAcceptedRender']['visual'] or '-')}<br>"
            f"{html.escape(row['target'])}: <code>{html.escape(','.join(row['targetAcceptedRender']['tilesets']) or '-')}</code> "
            f"{html.escape(row['targetAcceptedRender']['visual'] or '-')}"
        )
        body.append(
            "<tr>"
            f"<td>{html.escape(row['source'])}</td>"
            f"<td>{html.escape(row['target'])}</td>"
            f"<td>{html.escape(row['promotionRisk'])}</td>"
            f"<td>{gates}</td>"
            f"<td>{record_tilesets}</td>"
            f"<td>{accepted}</td>"
            f"<td>{html.escape(row['note'])}</td>"
            "</tr>"
        )
    return "\n".join([
        "<!doctype html>",
        '<html lang="en">',
        "<head>",
        '  <meta charset="utf-8">',
        '  <meta name="viewport" content="width=device-width, initial-scale=1">',
        "  <title>Save Selector Frontier Branches</title>",
        "  <style>",
        "    body { margin: 24px; background: #101010; color: #eee; font: 14px system-ui, sans-serif; }",
        "    table { border-collapse: collapse; width: 100%; }",
        "    th, td { border: 1px solid #333; padding: 6px 8px; vertical-align: top; }",
        "    th { background: #1d1d1d; }",
        "    code { color: #f5d76e; }",
        "  </style>",
        "</head>",
        "<body>",
        "  <h1>Save Selector Frontier Branches</h1>",
        "  <p>Branch gates and render-record mismatches for save-selector frontier edges.</p>",
        "  <table><thead><tr><th>source</th><th>target</th><th>risk</th><th>branch gates</th><th>record tilesets</th><th>accepted render</th><th>note</th></tr></thead>",
        f"  <tbody>{''.join(body) or '<tr><td colspan=\"7\">No rows.</td></tr>'}</tbody></table>",
        "</body>",
        "</html>",
        "",
    ])


def write_outputs(rows: list[dict], out_dir: Path = OUT) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "save_selector_frontier_branches.json").write_text(
        json.dumps(rows, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--frontier", type=Path, default=OUT / "save_selector_frontier.json")
    parser.add_argument("--stream-traces", type=Path, default=OUT / "save_selector_stream_traces.json")
    parser.add_argument("--leaf-streams", type=Path, default=OUT / "save_selector_leaf_streams.json")
    parser.add_argument("--render-reviews", type=Path, default=OUT / "map_render_reviews.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    rows = build_rows(
        json.loads(args.frontier.read_text(encoding="utf-8")),
        json.loads(args.stream_traces.read_text(encoding="utf-8")),
        json.loads(args.leaf_streams.read_text(encoding="utf-8")),
        json.loads(args.render_reviews.read_text(encoding="utf-8")),
    )
    write_outputs(rows, args.out_dir)
    print(f"wrote {len(rows)} save selector frontier branch rows -> {args.out_dir / 'save_selector_frontier_branches.json'}")


if __name__ == "__main__":
    main()
