#!/usr/bin/env python3
"""Summarize the static-default effect of opcode 0x24 mode 1 on object+0x61."""
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"
FAILED_OPCODE24_MODE1_DEFAULT_EFFECT_GATE_IDS = [
    "runtime-mode1-source-producer",
    "object61-route-control-effect",
    "strict-source-hotspot",
]
OPCODE24_MODE1_DEFAULT_EFFECT_MISSING_EVIDENCE = [
    "runtime write/read trace for 0x0059e348 around opcode 0x24 mode 1",
    "proof that object+0x61 selects a current-route control-flow target",
    "strict map1_01a source coordinate or event hotspot for map2_02d",
]
OPCODE24_MODE1_DEFAULT_EFFECT_EVIDENCE_REFS = [
    {"path": "out/save_selector_opcode24_mode1_runtime_context.json", "description": "mode1 source runtime-producer proof gap"},
    {"path": "out/save_selector_object61_consumers.json", "description": "object+0x61 consumer groups and route impact"},
    {"path": "out/save_selector_object61_stream_operands.json", "description": "current route object+0x61 stream operand scan"},
    {"path": "out/runtime_trace_feasibility.json", "description": "runtime trace availability blocker"},
]


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


def hex_byte(value: int | None) -> str | None:
    if value is None:
        return None
    return f"0x{value & 0xff:02x}"


def build_summary(
    opcode24_handler: dict,
    mode1_runtime_context: dict,
    object61_consumers: dict,
    object61_stream_operands: dict,
) -> dict:
    storage = mode1_runtime_context.get("storage") or {}
    current_boundary = opcode24_handler.get("currentBoundary") or {}
    current_mode = opcode24_handler.get("currentMode") or {}
    source_value = storage.get("staticInitialValue")
    default_object61_value = source_value + 3 if isinstance(source_value, int) else None
    branch_rows = object61_stream_operands.get("branchCapableRows") or []
    branch = branch_rows[0] if branch_rows else {}
    object_groups = [
        {
            "name": group.get("name"),
            "range": group.get("range"),
            "routeImpact": group.get("routeImpact"),
        }
        for group in object61_consumers.get("groups") or []
    ]
    conclusion = (
        "With the current static evidence, opcode 0x24 mode 1 reads the zero-filled byte 0x0059e348 and would write "
        "object+0x61 = 3 at process start. The existing object+0x61 consumers use that field as linked-object state, "
        "and the current map1_01a->map2_02d stream operands contain no frontier leaf, frontier reader, or false-target "
        "operand. The only branch-capable row is opcode 0x2f at 0x00542e74, whose dword [stream+4] is 0x00001618 "
        "for map2_16j.cns. Therefore the static/default mode1 effect is not route promotion evidence; a runtime "
        "producer trace for 0x0059e348 or a strict map1_01a hotspot is still required."
    )
    return {
        "source": SOURCE,
        "target": TARGET,
        "opcodeVaHex": current_boundary.get("opcodeVaHex"),
        "opcodeValueHex": current_boundary.get("opcodeValueHex"),
        "currentModeByteHex": current_boundary.get("streamPlus1Hex"),
        "currentModeTargetHex": current_mode.get("targetVaHex"),
        "mode1SourceHex": mode1_runtime_context.get("mode1SourceHex"),
        "mode1SourceStaticInitialValueHex": storage.get("staticInitialValueHex"),
        "mode1SourceStaticInitialValueKind": storage.get("staticInitialValueKind"),
        "mode1SourceHasRawByte": storage.get("mode1SourceHasRawByte"),
        "mode1SourceSaveBacked": mode1_runtime_context.get("notSavedataBacked") is False,
        "mode1SourceHasStaticProducer": mode1_runtime_context.get("noStaticProducer") is False,
        "defaultObject61Value": default_object61_value,
        "defaultObject61ValueHex": hex_byte(default_object61_value),
        "object61ConsumerGroupCount": len(object_groups),
        "object61ConsumerGroups": object_groups,
        "objectHandlerOpcodeCount": object61_stream_operands.get("objectHandlerOpcodeCount"),
        "routeOperandRowCount": object61_stream_operands.get("routeOperandRowCount"),
        "branchCapableRowCount": object61_stream_operands.get("branchCapableRowCount"),
        "directFrontierOperandCount": object61_stream_operands.get("directFrontierOperandCount"),
        "branchFrontierOperandCount": object61_stream_operands.get("branchFrontierOperandCount"),
        "branchOperand": {
            "wordVaHex": branch.get("wordVaHex"),
            "opcodeHex": branch.get("opcodeHex"),
            "handlerVaHex": branch.get("handlerVaHex"),
            "valueHex": branch.get("valueHex"),
            "valueMeaning": branch.get("valueMeaning"),
            "nextDwordHex": branch.get("nextDwordHex"),
            "nextDwordKind": branch.get("nextDwordKind"),
            "cns": branch.get("cns"),
        },
        "staticDefaultPromotesRoute": False,
        "runtimeProducerRequired": True,
        "strictHotspotRequired": True,
        "proofFound": False,
        "opcode24Mode1DefaultEffectProofFound": False,
        "failedOpcode24Mode1DefaultEffectGateIds": FAILED_OPCODE24_MODE1_DEFAULT_EFFECT_GATE_IDS,
        "missingEvidence": OPCODE24_MODE1_DEFAULT_EFFECT_MISSING_EVIDENCE,
        "evidenceRefs": OPCODE24_MODE1_DEFAULT_EFFECT_EVIDENCE_REFS,
        "evidenceRefCount": len(OPCODE24_MODE1_DEFAULT_EFFECT_EVIDENCE_REFS),
        "promotionStatus": "blocked",
        "remainingProofs": [
            "capture a runtime write/read trace for 0x0059e348 around opcode 0x24 mode 1",
            "prove object+0x61 selects a current-route control-flow target, if it ever does",
            "find a strict map1_01a source coordinate or event hotspot for map2_02d",
        ],
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    branch = summary.get("branchOperand") or {}
    lines = [
        "# Save Selector Opcode 0x24 Mode1 Default Effect",
        "",
        f"- route: `{summary['source']} -> {summary['target']}`",
        f"- opcode: `{summary.get('opcodeVaHex')}` value `{summary.get('opcodeValueHex')}`",
        f"- mode target: `{summary.get('currentModeTargetHex')}`",
        f"- mode1 source: `{summary.get('mode1SourceHex')}`",
        f"- static source value: `{summary.get('mode1SourceStaticInitialValueHex')}` ({summary.get('mode1SourceStaticInitialValueKind')})",
        f"- default object+0x61 value: `{summary.get('defaultObject61ValueHex')}`",
        f"- save-backed source: {summary.get('mode1SourceSaveBacked')}",
        f"- static source producer: {summary.get('mode1SourceHasStaticProducer')}",
        f"- object+0x61 consumer groups: {summary.get('object61ConsumerGroupCount')}",
        f"- route operand rows: {summary.get('routeOperandRowCount')}",
        f"- direct frontier operands: {summary.get('directFrontierOperandCount')}",
        f"- branch frontier operands: {summary.get('branchFrontierOperandCount')}",
        f"- branch operand: `{branch.get('wordVaHex')}` opcode `{branch.get('opcodeHex')}` next `{branch.get('nextDwordHex')}` `{branch.get('cns')}`",
        f"- static default promotes route: {summary.get('staticDefaultPromotesRoute')}",
        f"- proofFound: `{summary['proofFound']}`",
        f"- opcode24Mode1DefaultEffectProofFound: `{summary['opcode24Mode1DefaultEffectProofFound']}`",
        f"- promotion status: `{summary.get('promotionStatus')}`",
        "",
        summary["conclusion"],
        "",
        "## Failed Gates",
        "",
    ]
    lines.extend(f"- `{item}`" for item in summary["failedOpcode24Mode1DefaultEffectGateIds"])
    lines.extend([
        "",
        "## Missing Evidence",
        "",
    ])
    lines.extend(f"- {item}" for item in summary["missingEvidence"])
    lines.extend([
        "",
        "## Evidence Refs",
        "",
    ])
    lines.extend(
        f"- `{row['path']}`: {row['description']}"
        for row in summary["evidenceRefs"]
    )
    lines.extend([
        "",
        "## Object+0x61 Consumer Groups",
        "",
        "| group | range | route impact |",
        "| --- | --- | --- |",
    ])
    for group in summary.get("object61ConsumerGroups") or []:
        lines.append(f"| {group.get('name')} | `{group.get('range')}` | {group.get('routeImpact')} |")
    lines.extend(["", "## Remaining Proofs", ""])
    lines.extend(f"- {item}" for item in summary.get("remainingProofs") or [])
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    branch = summary.get("branchOperand") or {}
    groups = "\n".join(
        "<tr>"
        f"<td>{html.escape(str(group.get('name')))}</td>"
        f"<td><code>{html.escape(str(group.get('range')))}</code></td>"
        f"<td>{html.escape(str(group.get('routeImpact')))}</td>"
        "</tr>"
        for group in summary.get("object61ConsumerGroups") or []
    )
    proofs = "\n".join(f"<li>{html.escape(str(item))}</li>" for item in summary.get("remainingProofs") or [])
    failed_gates = "".join(
        f"<li><code>{html.escape(item)}</code></li>"
        for item in summary["failedOpcode24Mode1DefaultEffectGateIds"]
    )
    missing_evidence = "".join(
        f"<li>{html.escape(item)}</li>"
        for item in summary["missingEvidence"]
    )
    evidence_refs = "".join(
        f"<li><code>{html.escape(row['path'])}</code>: {html.escape(row['description'])}</li>"
        for row in summary["evidenceRefs"]
    )
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Opcode 0x24 Mode1 Default Effect</title>",
        "<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee}table{border-collapse:collapse;max-width:1400px}td,th{border:1px solid #444;padding:6px 8px;vertical-align:top}code{color:#9bd4ff}</style>",
        "<h1>Save Selector Opcode 0x24 Mode1 Default Effect</h1>",
        "<ul>",
        f"<li>route: <code>{html.escape(summary['source'])} -&gt; {html.escape(summary['target'])}</code></li>",
        f"<li>opcode: <code>{html.escape(str(summary.get('opcodeVaHex')))}</code> value <code>{html.escape(str(summary.get('opcodeValueHex')))}</code></li>",
        f"<li>mode target: <code>{html.escape(str(summary.get('currentModeTargetHex')))}</code></li>",
        f"<li>mode1 source: <code>{html.escape(str(summary.get('mode1SourceHex')))}</code></li>",
        f"<li>static source value: <code>{html.escape(str(summary.get('mode1SourceStaticInitialValueHex')))}</code> ({html.escape(str(summary.get('mode1SourceStaticInitialValueKind')))})</li>",
        f"<li>default object+0x61 value: <code>{html.escape(str(summary.get('defaultObject61ValueHex')))}</code></li>",
        f"<li>save-backed source: {summary.get('mode1SourceSaveBacked')}</li>",
        f"<li>static source producer: {summary.get('mode1SourceHasStaticProducer')}</li>",
        f"<li>object+0x61 consumer groups: {summary.get('object61ConsumerGroupCount')}</li>",
        f"<li>route operand rows: {summary.get('routeOperandRowCount')}</li>",
        f"<li>direct frontier operands: {summary.get('directFrontierOperandCount')}</li>",
        f"<li>branch frontier operands: {summary.get('branchFrontierOperandCount')}</li>",
        f"<li>branch operand: <code>{html.escape(str(branch.get('wordVaHex')))}</code> opcode <code>{html.escape(str(branch.get('opcodeHex')))}</code> next <code>{html.escape(str(branch.get('nextDwordHex')))}</code> <code>{html.escape(str(branch.get('cns')))}</code></li>",
        f"<li>static default promotes route: {summary.get('staticDefaultPromotesRoute')}</li>",
        f"<li>proofFound: <code>{html.escape(str(summary['proofFound']))}</code></li>",
        f"<li>promotion status: <code>{html.escape(str(summary.get('promotionStatus')))}</code></li>",
        "</ul>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<h2>Failed Gates</h2>",
        f"<ul>{failed_gates}</ul>",
        "<h2>Missing Evidence</h2>",
        f"<ul>{missing_evidence}</ul>",
        "<h2>Evidence Refs</h2>",
        f"<ul>{evidence_refs}</ul>",
        "<h2>Object+0x61 Consumer Groups</h2>",
        "<table><thead><tr><th>group</th><th>range</th><th>route impact</th></tr></thead><tbody>",
        groups,
        "</tbody></table>",
        "<h2>Remaining Proofs</h2><ul>",
        proofs,
        "</ul>",
    ])


def write_outputs(summary: dict, out_dir: Path = OUT, html_out: Path | None = None) -> Path:
    out_dir.mkdir(parents=True, exist_ok=True)
    json_out = out_dir / "save_selector_opcode24_mode1_default_effect.json"
    json_out.write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    if html_out is not None:
        html_out.parent.mkdir(parents=True, exist_ok=True)
        html_out.write_text(html_page(summary), encoding="utf-8")
    return json_out


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--out-dir", type=Path, default=OUT)
    parser.add_argument("--html-out", type=Path)
    args = parser.parse_args()
    summary = build_summary(
        load_json(args.out_dir / "save_selector_opcode24_handler.json", {}),
        load_json(args.out_dir / "save_selector_opcode24_mode1_runtime_context.json", {}),
        load_json(args.out_dir / "save_selector_object61_consumers.json", {}),
        load_json(args.out_dir / "save_selector_object61_stream_operands.json", {}),
    )
    json_out = write_outputs(summary, args.out_dir, args.html_out)
    print(f"wrote opcode24 mode1 default effect -> {json_out}")


if __name__ == "__main__":
    main()
