#!/usr/bin/env python3
"""Classify post-gate opcode 0x10 rows near the current route blocker."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
SOURCE = "map1_01a"
TARGET = "map2_02d"
OPCODE10_HANDLER = "0x0040b49e"


def load_json(path: Path, fallback: Any) -> Any:
    if not path.exists():
        return fallback
    return json.loads(path.read_text(encoding="utf-8"))


def decode_op10(value_hex: str | None) -> dict:
    value = int(value_hex or "0", 16)
    stream_plus_1 = (value >> 8) & 0xFF
    helper_arg = (value >> 16) & 0xFF
    return {
        "streamPlus1Hex": f"0x{stream_plus_1:02x}",
        "helperArgumentHex": f"0x{helper_arg:02x}",
        "stateTable": "primaryBranchState" if stream_plus_1 == 0 else "secondaryBranchState",
        "helperDispatchValid": helper_arg <= 0x0B,
    }


def route_gate_path(gate_paths: list[dict]) -> dict:
    return next(
        (
            row for row in gate_paths
            if row.get("source") == SOURCE and row.get("target") == TARGET
        ),
        {},
    )


def secondary_candidate_by_va(secondary_sources: dict) -> dict[str, dict]:
    return {
        row.get("vaHex"): row
        for row in secondary_sources.get("candidates") or []
        if row.get("vaHex")
    }


def build_summary(gate_paths: list[dict] | None = None, secondary_sources: dict | None = None) -> dict:
    gate_paths = gate_paths if gate_paths is not None else load_json(OUT / "save_selector_gate_paths.json", [])
    secondary_sources = secondary_sources if secondary_sources is not None else load_json(
        OUT / "save_selector_secondary_state_sources.json",
        {},
    )
    gate_path = route_gate_path(gate_paths)
    candidates_by_va = secondary_candidate_by_va(secondary_sources)
    post_gate_trace = gate_path.get("postGateTrace") or []
    post_gate_opcode10_rows = []
    for row in post_gate_trace:
        if row.get("opcodeHex") != "0x10":
            continue
        decoded = decode_op10(row.get("valueHex"))
        secondary_candidate = candidates_by_va.get(row.get("vaHex")) or {}
        helper_valid = secondary_candidate.get("helperDispatchValid", decoded["helperDispatchValid"])
        state_table = secondary_candidate.get("stateTable", decoded["stateTable"])
        post_gate_opcode10_rows.append({
            "vaHex": row.get("vaHex"),
            "valueHex": row.get("valueHex"),
            "handlerVaHex": row.get("handlerVaHex"),
            "streamPlus1Hex": secondary_candidate.get("streamPlus1Hex", decoded["streamPlus1Hex"]),
            "helperArgumentHex": secondary_candidate.get("helperArgumentHex", decoded["helperArgumentHex"]),
            "stateTable": state_table,
            "helperDispatchValid": helper_valid,
            "beforeFrontierReader": secondary_candidate.get("beforeFrontierReader"),
            "routeRelevance": secondary_candidate.get("routeRelevance") or (
                "post-gate opcode 0x10 row does not dispatch a valid helper case"
                if not helper_valid
                else "post-gate opcode 0x10 row dispatches a valid helper case"
            ),
            "canResetSecondaryBranchState": state_table == "secondaryBranchState" and bool(helper_valid),
        })
    valid_post_gate_resets = [
        row for row in post_gate_opcode10_rows
        if row["canResetSecondaryBranchState"]
    ]
    conclusion = (
        "The two opcode 0x10 rows immediately after the current gates are secondary-table shaped, but their "
        "helper arguments are 0xe6 and 0xe7, outside the valid helper jump-table range 0x00..0x0b. They therefore "
        "do not prove a post-gate secondaryBranchState reset or fill. The path still stops at the opcode 0x24/"
        "0xe8 data-descriptor boundary before any proven route to the 0x00542b0c frontier reader."
    )
    return {
        "source": SOURCE,
        "target": TARGET,
        "nearestWriterVaHex": gate_path.get("nearestWriterVaHex"),
        "frontierReaderVaHex": gate_path.get("frontierReaderVaHex"),
        "postGateTraceStartHex": (post_gate_trace[0] or {}).get("vaHex") if post_gate_trace else None,
        "opcode24BoundaryVaHex": (gate_path.get("opcode24Boundary") or {}).get("opcodeVaHex"),
        "dispatchStopVaHex": gate_path.get("dispatchStopVaHex"),
        "dispatchStopOpcodeHex": gate_path.get("dispatchStopOpcodeHex"),
        "dispatchStopHandlerSection": gate_path.get("dispatchStopHandlerSection"),
        "helperValidCaseRangeHex": secondary_sources.get("helperValidCaseRangeHex"),
        "postGateOpcode10RowCount": len(post_gate_opcode10_rows),
        "validPostGateSecondaryResetCount": len(valid_post_gate_resets),
        "invalidPostGateOpcode10RowCount": len(post_gate_opcode10_rows) - len(valid_post_gate_resets),
        "postGateOpcode10Rows": post_gate_opcode10_rows,
        "currentRootValidBeforeFrontierCount": secondary_sources.get("validBeforeFrontierCount"),
        "currentRootValidAfterFrontierCount": secondary_sources.get("validAfterFrontierCount"),
        "validAfterFrontierRows": secondary_sources.get("validAfterFrontier") or [],
        "postGateRowsPromoteRoute": False,
        "controlPathProofStatus": "blocked",
        "promotionStatus": "blocked",
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Post-Gate Reset Candidates",
        "",
        f"- route: {summary['source']} -> {summary['target']}",
        f"- nearest writer: `{summary['nearestWriterVaHex']}`",
        f"- frontier reader: `{summary['frontierReaderVaHex']}`",
        f"- post-gate trace start: `{summary['postGateTraceStartHex']}`",
        f"- opcode 0x24 boundary: `{summary['opcode24BoundaryVaHex']}`",
        f"- dispatch stop: `{summary['dispatchStopVaHex']}` opcode `{summary['dispatchStopOpcodeHex']}` in {summary['dispatchStopHandlerSection']}",
        f"- helper valid cases: `{summary['helperValidCaseRangeHex']}`",
        f"- post-gate opcode 0x10 rows: {summary['postGateOpcode10RowCount']}",
        f"- valid post-gate secondary resets: {summary['validPostGateSecondaryResetCount']}",
        f"- current valid before-frontier secondary fills: {summary['currentRootValidBeforeFrontierCount']}",
        f"- post-gate rows promote route: {summary['postGateRowsPromoteRoute']}",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        summary["conclusion"],
        "",
        "## Post-Gate Opcode 0x10 Rows",
        "",
        "| va | value | stream+1 | helper arg | table | helper valid | can reset secondary | relevance |",
        "| --- | --- | --- | --- | --- | --- | --- | --- |",
    ]
    for row in summary["postGateOpcode10Rows"]:
        lines.append(
            f"| `{row['vaHex']}` | `{row['valueHex']}` | `{row['streamPlus1Hex']}` | "
            f"`{row['helperArgumentHex']}` | {row['stateTable']} | {row['helperDispatchValid']} | "
            f"{row['canResetSecondaryBranchState']} | {row['routeRelevance']} |"
        )
    lines.extend([
        "",
        "## Valid After-Frontier Rows",
        "",
        "| va | value | helper arg | relevance |",
        "| --- | --- | --- | --- |",
    ])
    for row in summary["validAfterFrontierRows"]:
        lines.append(
            f"| `{row.get('vaHex')}` | `{row.get('valueHex')}` | `{row.get('helperArgumentHex')}` | "
            f"{row.get('routeRelevance')} |"
        )
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    post_rows = []
    for row in summary["postGateOpcode10Rows"]:
        post_rows.append(
            "<tr>"
            f"<td><code>{html.escape(row['vaHex'])}</code></td>"
            f"<td><code>{html.escape(row['valueHex'])}</code></td>"
            f"<td><code>{html.escape(row['streamPlus1Hex'])}</code></td>"
            f"<td><code>{html.escape(row['helperArgumentHex'])}</code></td>"
            f"<td>{html.escape(row['stateTable'])}</td>"
            f"<td>{row['helperDispatchValid']}</td>"
            f"<td>{row['canResetSecondaryBranchState']}</td>"
            f"<td>{html.escape(row['routeRelevance'])}</td>"
            "</tr>"
        )
    after_rows = []
    for row in summary["validAfterFrontierRows"]:
        after_rows.append(
            "<tr>"
            f"<td><code>{html.escape(row.get('vaHex') or '-')}</code></td>"
            f"<td><code>{html.escape(row.get('valueHex') or '-')}</code></td>"
            f"<td><code>{html.escape(row.get('helperArgumentHex') or '-')}</code></td>"
            f"<td>{html.escape(row.get('routeRelevance') or '-')}</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 Post-Gate Reset Candidates</title>",
        "  <style>",
        "    :root { color-scheme: dark; font-family: system-ui, sans-serif; background: #101010; color: #eee; }",
        "    body { margin: 0; padding: 24px; }",
        "    h1 { margin: 0 0 8px; font-size: 24px; }",
        "    h2 { margin: 24px 0 8px; font-size: 18px; }",
        "    p { max-width: 1120px; color: #bbb; line-height: 1.45; }",
        "    table { width: 100%; border-collapse: collapse; margin: 12px 0 20px; font-size: 13px; }",
        "    th, td { border-bottom: 1px solid #303030; padding: 7px 8px; text-align: left; vertical-align: top; }",
        "    th { position: sticky; top: 0; background: #181818; color: #ddd; }",
        "    code { color: #d7f0ff; }",
        "  </style>",
        "</head>",
        "<body>",
        "  <h1>Save Selector Post-Gate Reset Candidates</h1>",
        f"  <p>route {html.escape(summary['source'])} -&gt; {html.escape(summary['target'])}; post-gate opcode 0x10 rows {summary['postGateOpcode10RowCount']}; valid post-gate secondary resets {summary['validPostGateSecondaryResetCount']}; promotion status <code>{html.escape(summary['promotionStatus'])}</code>.</p>",
        f"  <p>opcode 0x24 boundary <code>{html.escape(summary['opcode24BoundaryVaHex'] or '-')}</code>; dispatch stop <code>{html.escape(summary['dispatchStopVaHex'] or '-')}</code>; post-gate rows promote route {summary['postGateRowsPromoteRoute']}.</p>",
        f"  <p>{html.escape(summary['conclusion'])}</p>",
        "  <h2>Post-Gate Opcode 0x10 Rows</h2>",
        "  <table><thead><tr><th>va</th><th>value</th><th>stream+1</th><th>helper arg</th><th>table</th><th>helper valid</th><th>can reset secondary</th><th>relevance</th></tr></thead><tbody>",
        *post_rows,
        "  </tbody></table>",
        "  <h2>Valid After-Frontier Rows</h2>",
        "  <table><thead><tr><th>va</th><th>value</th><th>helper arg</th><th>relevance</th></tr></thead><tbody>",
        *after_rows,
        "  </tbody></table>",
        "</body>",
        "</html>",
        "",
    ])


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


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--gate-paths", type=Path, default=OUT / "save_selector_gate_paths.json")
    parser.add_argument("--secondary-sources", type=Path, default=OUT / "save_selector_secondary_state_sources.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_summary(
        load_json(args.gate_paths, []),
        load_json(args.secondary_sources, {}),
    )
    write_outputs(summary, args.out_dir)
    print(
        "wrote save selector post-gate reset candidates -> "
        f"{args.out_dir / 'save_selector_post_gate_reset_candidates.json'}"
    )


if __name__ == "__main__":
    main()
