#!/usr/bin/env python3
"""Enumerate opcode 0x20 active-order space for the current gate-base proof."""
from __future__ import annotations

import argparse
import html
import itertools
import json
from pathlib import Path
from typing import Any, Iterable


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
SOURCE = "map1_01a"
TARGET = "map2_02d"
FAILED_OPCODE20_ORDER_SPACE_GATE_IDS = [
    "selector-2-0-active-order",
    "runtime-descriptor-object-state",
    "direct-route-proof-in-descriptor-order",
    "strict-source-hotspot",
]
OPCODE20_ORDER_SPACE_MISSING_EVIDENCE = [
    "selector 2:0 active order/count or equivalent runtime trace",
    "runtime descriptor/object state selected by opcode 0x20",
    "field-map/frontier/gate direct proof in an enumerated descriptor order",
    "strict map1_01a source hotspot",
]
OPCODE20_ORDER_SPACE_EVIDENCE_REFS = [
    {
        "path": "out/save_selector_opcode20_descriptor_scripts.json",
        "description": "descriptor script scans used to enumerate direct route proof absence",
    },
    {
        "path": "out/save_selector_opcode20_slot_sources.json",
        "description": "loaded slot capacity and runtime slot descriptor requirements",
    },
    {
        "path": "out/save_selector_opcode20_sample_order_effects.json",
        "description": "known public sample order signatures and current selector sample coverage",
    },
]


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


def descriptor_rows_by_index(descriptor_scripts: dict) -> dict[int, dict]:
    return {
        row["index"]: row
        for row in descriptor_scripts.get("descriptorRows") or []
        if isinstance(row.get("index"), int)
    }


def histogram(values: Iterable[str | None]) -> list[dict]:
    counts: dict[str, int] = {}
    for value in values:
        key = value or "none"
        counts[key] = counts.get(key, 0) + 1
    return [
        {"value": value, "count": count}
        for value, count in sorted(counts.items())
    ]


def iter_orders(indices: list[int], max_slots: int, repeat_allowed: bool) -> Iterable[tuple[int, ...]]:
    for count in range(1, max_slots + 1):
        if repeat_allowed:
            yield from itertools.product(indices, repeat=count)
        else:
            yield from itertools.permutations(indices, count)


def row_positive(row: dict, key: str) -> bool:
    return (row.get(key) or 0) > 0


def summarize_order(order: tuple[int, ...], descriptors: dict[int, dict]) -> dict:
    final_shaped = None
    final_non_pointer = None
    has_field_record = False
    has_frontier_ref = False
    has_gate_writer = False
    has_gate_reader = False
    has_non_pointer_context = False
    for descriptor_index in order:
        descriptor = descriptors[descriptor_index]
        has_field_record = has_field_record or row_positive(descriptor, "script4FieldRecordCount")
        has_frontier_ref = has_frontier_ref or row_positive(descriptor, "script4CurrentFrontierDirectRefCount")
        has_gate_writer = has_gate_writer or row_positive(descriptor, "script4GateWriterCount")
        has_gate_reader = has_gate_reader or row_positive(descriptor, "script4GateReaderCount")
        shaped = descriptor.get("script4LastContextA8SetterRow")
        non_pointer = descriptor.get("script4LastNonPointerContextA8SetterRow")
        if shaped:
            final_shaped = shaped
        if non_pointer:
            final_non_pointer = non_pointer
            has_non_pointer_context = True
    return {
        "order": list(order),
        "hasFieldRecord": has_field_record,
        "hasCurrentFrontierRef": has_frontier_ref,
        "hasGateWriter": has_gate_writer,
        "hasGateReader": has_gate_reader,
        "hasNonPointerContextA8Setter": has_non_pointer_context,
        "finalShapedContextA8BaseExpression": (final_shaped or {}).get("baseExpression"),
        "finalShapedContextA8IsPointerDword": (final_shaped or {}).get("isPointerDword"),
        "finalNonPointerContextA8BaseExpression": (final_non_pointer or {}).get("baseExpression"),
    }


def summarize_space(indices: list[int], max_slots: int, descriptors: dict[int, dict], repeat_allowed: bool) -> dict:
    rows = [
        summarize_order(order, descriptors)
        for order in iter_orders(indices, max_slots, repeat_allowed)
    ]
    direct_proof_rows = [
        row for row in rows
        if row["hasFieldRecord"]
        or row["hasCurrentFrontierRef"]
        or row["hasGateWriter"]
        or row["hasGateReader"]
    ]
    return {
        "repeatAllowed": repeat_allowed,
        "orderCount": len(rows),
        "fieldRecordOrderCount": sum(1 for row in rows if row["hasFieldRecord"]),
        "currentFrontierRefOrderCount": sum(1 for row in rows if row["hasCurrentFrontierRef"]),
        "gateWriterOrderCount": sum(1 for row in rows if row["hasGateWriter"]),
        "gateReaderOrderCount": sum(1 for row in rows if row["hasGateReader"]),
        "directProofOrderCount": len(direct_proof_rows),
        "nonPointerContextOrderCount": sum(1 for row in rows if row["hasNonPointerContextA8Setter"]),
        "finalShapedContextA8BaseHistogram": histogram(row["finalShapedContextA8BaseExpression"] for row in rows),
        "finalNonPointerContextA8BaseHistogram": histogram(row["finalNonPointerContextA8BaseExpression"] for row in rows),
        "finalShapedPointerDwordCount": sum(1 for row in rows if row["finalShapedContextA8IsPointerDword"] is True),
        "sampleRows": rows[:16],
    }


def build_summary(
    descriptor_scripts: dict | None = None,
    slot_sources: dict | None = None,
    sample_order_effects: dict | None = None,
) -> dict:
    descriptor_scripts = descriptor_scripts if descriptor_scripts is not None else load_json(
        OUT / "save_selector_opcode20_descriptor_scripts.json",
        {},
    )
    slot_sources = slot_sources if slot_sources is not None else load_json(
        OUT / "save_selector_opcode20_slot_sources.json",
        {},
    )
    sample_order_effects = sample_order_effects if sample_order_effects is not None else load_json(
        OUT / "save_selector_opcode20_sample_order_effects.json",
        {},
    )
    descriptors = descriptor_rows_by_index(descriptor_scripts)
    indices = sorted(descriptors)
    max_slots = int(slot_sources.get("loadedCompleteSlotCapacity") or 3)
    unique_space = summarize_space(indices, max_slots, descriptors, repeat_allowed=False)
    repeat_space = summarize_space(indices, max_slots, descriptors, repeat_allowed=True)
    direct_proof_count = unique_space["directProofOrderCount"] + repeat_space["directProofOrderCount"]
    conclusion = (
        "Enumerating every descriptor active-order shape up to the loaded three-slot capacity still finds no static "
        "field-map record, current-frontier reference, or 0xe8/0xea gate reader/writer in descriptor+4 scripts. "
        "The only non-pointer context+0xa8 base that can be selected by active order alone is the generic runtime "
        "object-pointer table path. The 0x0059e310 rows are pointer-dword low-byte collisions in the shaped scan, "
        "not reliable script opcodes. Active order alone therefore cannot prove the gate-time base for "
        "map1_01a->map2_02d; runtime descriptor/object state or an equivalent trace is still required."
    )
    return {
        "source": SOURCE,
        "target": TARGET,
        "descriptorRowCount": len(indices),
        "descriptorIndices": indices,
        "slotCapacity": max_slots,
        "uniqueOrderSpace": unique_space,
        "repeatAllowedOrderSpace": repeat_space,
        "knownSampleSignatures": sample_order_effects.get("uniqueSampleSignatures") or [],
        "knownSampleStateCount": sample_order_effects.get("uniqueSampleStateCount"),
        "currentFrontierSampleCovered": sample_order_effects.get("currentFrontierSampleCovered"),
        "allEnumeratedOrdersLackDirectRouteProof": direct_proof_count == 0,
        "activeOrderAlonePromotesRoute": False,
        "runtimeDescriptorObjectStateRequired": True,
        "proofFound": False,
        "opcode20OrderSpaceProofFound": False,
        "failedOpcode20OrderSpaceGateIds": FAILED_OPCODE20_ORDER_SPACE_GATE_IDS,
        "missingEvidence": OPCODE20_ORDER_SPACE_MISSING_EVIDENCE,
        "evidenceRefs": OPCODE20_ORDER_SPACE_EVIDENCE_REFS,
        "evidenceRefCount": len(OPCODE20_ORDER_SPACE_EVIDENCE_REFS),
        "promotionStatus": "blocked",
        "remainingProofs": [
            "capture selector 2:0 active order/count or an equivalent runtime trace",
            "prove the runtime object pointer selected by descriptor+4 if the 0x42 path is active",
            "find a strict map1_01a source hotspot or equivalent non-coordinate trigger",
        ],
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    unique = summary["uniqueOrderSpace"]
    repeat = summary["repeatAllowedOrderSpace"]
    lines = [
        "# Save Selector Opcode 0x20 Active Order Space",
        "",
        f"- route: `{summary['source']} -> {summary['target']}`",
        f"- descriptor rows: {summary['descriptorRowCount']}",
        f"- loaded slot capacity: {summary['slotCapacity']}",
        f"- unique-index orders: {unique['orderCount']}",
        f"- repeat-allowed orders: {repeat['orderCount']}",
        f"- unique direct proof orders: {unique['directProofOrderCount']}",
        f"- repeat-allowed direct proof orders: {repeat['directProofOrderCount']}",
        f"- current selector sample covered: {summary['currentFrontierSampleCovered']}",
        f"- active order alone promotes route: {summary['activeOrderAlonePromotesRoute']}",
        f"- proofFound: `{summary['proofFound']}`",
        f"- opcode20OrderSpaceProofFound: `{summary['opcode20OrderSpaceProofFound']}`",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        summary["conclusion"],
        "",
        "## Failed Gates",
        "",
    ]
    lines.extend(f"- `{item}`" for item in summary["failedOpcode20OrderSpaceGateIds"])
    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([
        "",
        "## Order Space Counts",
        "",
        "| space | orders | field records | frontier refs | gate writers | gate readers | non-pointer context rows | shaped pointer rows |",
        "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |",
    ])
    for label, space in (("unique-index", unique), ("repeat-allowed", repeat)):
        lines.append(
            f"| {label} | {space['orderCount']} | {space['fieldRecordOrderCount']} | "
            f"{space['currentFrontierRefOrderCount']} | {space['gateWriterOrderCount']} | "
            f"{space['gateReaderOrderCount']} | {space['nonPointerContextOrderCount']} | "
            f"{space['finalShapedPointerDwordCount']} |"
        )
    lines.extend(["", "## Final Non-Pointer Context+0xa8 Bases", "", "| space | base | orders |", "| --- | --- | ---: |"])
    for label, space in (("unique-index", unique), ("repeat-allowed", repeat)):
        for row in space["finalNonPointerContextA8BaseHistogram"]:
            lines.append(f"| {label} | `{row['value']}` | {row['count']} |")
    lines.extend(["", "## Known Sample Signatures", ""])
    lines.extend(f"- `{item}`" for item in summary.get("knownSampleSignatures") or [])
    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:
    unique = summary["uniqueOrderSpace"]
    repeat = summary["repeatAllowedOrderSpace"]
    failed_gates = "\n".join(
        f"<li><code>{html.escape(item)}</code></li>"
        for item in summary["failedOpcode20OrderSpaceGateIds"]
    )
    missing_evidence = "\n".join(
        f"<li>{html.escape(item)}</li>"
        for item in summary["missingEvidence"]
    )
    evidence_refs = "\n".join(
        f"<li><code>{html.escape(row['path'])}</code>: {html.escape(row['description'])}</li>"
        for row in summary["evidenceRefs"]
    )
    count_rows = []
    for label, space in (("unique-index", unique), ("repeat-allowed", repeat)):
        count_rows.append(
            "<tr>"
            f"<td>{label}</td>"
            f"<td>{space['orderCount']}</td>"
            f"<td>{space['fieldRecordOrderCount']}</td>"
            f"<td>{space['currentFrontierRefOrderCount']}</td>"
            f"<td>{space['gateWriterOrderCount']}</td>"
            f"<td>{space['gateReaderOrderCount']}</td>"
            f"<td>{space['nonPointerContextOrderCount']}</td>"
            f"<td>{space['finalShapedPointerDwordCount']}</td>"
            "</tr>"
        )
    base_rows = []
    for label, space in (("unique-index", unique), ("repeat-allowed", repeat)):
        for row in space["finalNonPointerContextA8BaseHistogram"]:
            base_rows.append(
                "<tr>"
                f"<td>{label}</td>"
                f"<td><code>{html.escape(row['value'])}</code></td>"
                f"<td>{row['count']}</td>"
                "</tr>"
            )
    samples = "\n".join(
        f"<li><code>{html.escape(str(item))}</code></li>"
        for item in summary.get("knownSampleSignatures") or []
    )
    proofs = "\n".join(f"<li>{html.escape(item)}</li>" for item in summary.get("remainingProofs") or [])
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Opcode 0x20 Active Order Space</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 0x20 Active Order Space</h1>",
        "<ul>",
        f"<li>route: <code>{html.escape(summary['source'])} -&gt; {html.escape(summary['target'])}</code></li>",
        f"<li>descriptor rows: {summary['descriptorRowCount']}</li>",
        f"<li>loaded slot capacity: {summary['slotCapacity']}</li>",
        f"<li>unique-index orders: {unique['orderCount']}</li>",
        f"<li>repeat-allowed orders: {repeat['orderCount']}</li>",
        f"<li>unique direct proof orders: {unique['directProofOrderCount']}</li>",
        f"<li>repeat-allowed direct proof orders: {repeat['directProofOrderCount']}</li>",
        f"<li>current selector sample covered: {summary['currentFrontierSampleCovered']}</li>",
        f"<li>active order alone promotes route: {summary['activeOrderAlonePromotesRoute']}</li>",
        f"<li>proofFound: <code>{summary['proofFound']}</code></li>",
        f"<li>promotion status: <code>{html.escape(summary['promotionStatus'])}</code></li>",
        "</ul>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<h2>Failed Gates</h2><ul>",
        failed_gates,
        "</ul><h2>Missing Evidence</h2><ul>",
        missing_evidence,
        "</ul><h2>Evidence Refs</h2><ul>",
        evidence_refs,
        "</ul>",
        "<h2>Order Space Counts</h2>",
        "<table><thead><tr><th>space</th><th>orders</th><th>field records</th><th>frontier refs</th><th>gate writers</th><th>gate readers</th><th>non-pointer context rows</th><th>shaped pointer rows</th></tr></thead><tbody>",
        "".join(count_rows),
        "</tbody></table>",
        "<h2>Final Non-Pointer Context+0xa8 Bases</h2>",
        "<table><thead><tr><th>space</th><th>base</th><th>orders</th></tr></thead><tbody>",
        "".join(base_rows),
        "</tbody></table>",
        "<h2>Known Sample Signatures</h2><ul>",
        samples,
        "</ul><h2>Remaining Proofs</h2><ul>",
        proofs,
        "</ul>",
    ])


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


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_summary(
        load_json(args.out_dir / "save_selector_opcode20_descriptor_scripts.json", {}),
        load_json(args.out_dir / "save_selector_opcode20_slot_sources.json", {}),
        load_json(args.out_dir / "save_selector_opcode20_sample_order_effects.json", {}),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote opcode20 active order space -> {args.out_dir / 'save_selector_opcode20_order_space.html'}")


if __name__ == "__main__":
    main()
