#!/usr/bin/env python3
"""Compare current-root route-pair descriptors with the reader-bearing frontier descriptor."""
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"
FAILED_ROUTE_PAIR_DESCRIPTOR_GATE_IDS = [
    "normal-runtime-selector-root-execution",
    "reader-branch-target-linkage",
    "strict-source-hotspot",
]
ROUTE_PAIR_DESCRIPTOR_MISSING_EVIDENCE = [
    "normal runtime selector/root execution reaching corrected route-pair descriptors",
    "0x00542b0c reader branch outcome and field-map target linkage on a real route path",
    "strict map1_01a source coordinate or hotspot",
]
ROUTE_PAIR_DESCRIPTOR_EVIDENCE_REFS = [
    {
        "path": "out/save_selector_leaf_index_space.json",
        "fields": ["routeRelevantRows", "currentRoutePairDescriptorCount", "evidenceRefs", "evidenceRefCount"],
    },
    {
        "path": "out/save_selector_scene_record_sequence.json",
        "fields": ["rows", "sourceToTargetAdjacent", "geometryExitWordHits"],
    },
    {
        "path": "out/save_selector_stream_traces.json",
        "fields": ["streamVaHex", "trace", "stopReason"],
    },
    {
        "path": "out/save_selector_opcode2c_route_pair_context.json",
        "fields": ["rows", "correctedTrace", "correctedTraceReachesReaderCount"],
    },
]


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


def trace_brief(stream_traces: list[dict], stream_hex: str | None) -> dict:
    if not stream_hex:
        return {}
    row = next((item for item in stream_traces if item.get("streamVaHex") == stream_hex), None)
    if not row:
        return {}
    trace = row.get("trace") or []
    last = trace[-1] if trace else {}
    return {
        "streamKind": row.get("streamKind"),
        "streamVaHex": stream_hex,
        "stepCount": len(trace),
        "opcodes": [item.get("opcodeHex") for item in trace[:8] if item.get("opcodeHex")],
        "stopVaHex": last.get("vaHex"),
        "stopOpcodeHex": last.get("opcodeHex"),
        "stopReason": last.get("stopReason"),
        "containsFrontierReader": any(item.get("vaHex") == "0x00542b0c" for item in trace),
    }


def corrected_traces_by_descriptor(opcode2c_route_pair_context: dict | None) -> dict[str, dict]:
    if not opcode2c_route_pair_context:
        return {}
    return {
        row.get("descriptorHex"): row.get("correctedTrace") or {}
        for row in opcode2c_route_pair_context.get("rows") or []
        if row.get("descriptorHex")
    }


def build_summary(
    leaf_index: dict,
    scene_sequence: dict,
    stream_traces: list[dict],
    opcode2c_route_pair_context: dict | None = None,
) -> dict:
    corrected_by_descriptor = corrected_traces_by_descriptor(opcode2c_route_pair_context)
    sequence_by_leaf = {
        row.get("leafPointerHex"): row
        for row in scene_sequence.get("rows") or []
        if row.get("leafPointerHex")
    }
    route_rows = leaf_index.get("routeRelevantRows") or []
    current_route_pair_rows = [
        row for row in route_rows
        if row.get("insideCurrentRootEntryRun") and row.get("descriptorHasRoutePair")
    ]
    reader_rows = [
        row for row in route_rows
        if row.get("descriptorTraceContainsFrontierReader") or row.get("childTraceContainsFrontierReader")
    ]
    current_reader_rows = [row for row in reader_rows if row.get("insideCurrentRootEntryRun")]
    negative_reader_rows = [row for row in reader_rows if not row.get("insideCurrentRootEntryRun")]

    rows = []
    for row in route_rows:
        descriptor_hex = row.get("descriptorHex")
        child_hex = row.get("childPointerHex")
        sequence = sequence_by_leaf.get(descriptor_hex) or {}
        geometry_hits = sequence.get("geometryExitWordHits") or []
        corrected_trace = corrected_by_descriptor.get(descriptor_hex) or {}
        corrected_reaches_reader = corrected_trace.get("reachesFrontierReader") is True
        raw_reaches_reader = row.get("descriptorTraceContainsFrontierReader") is True
        rows.append({
            "index": row.get("rootRelativeIndex"),
            "entryVaHex": row.get("entryVaHex"),
            "insideCurrentRootEntryRun": row.get("insideCurrentRootEntryRun"),
            "descriptorHex": descriptor_hex,
            "childPointerHex": child_hex,
            "descriptorFieldMaps": row.get("descriptorFieldMaps") or [],
            "descriptorNestedFieldMaps": row.get("descriptorNestedFieldMaps") or [],
            "childFieldMaps": row.get("childFieldMaps") or [],
            "descriptorHasRoutePair": row.get("descriptorHasRoutePair"),
            "childHasRoutePair": row.get("childHasRoutePair"),
            "descriptorTraceContainsFrontierReader": row.get("descriptorTraceContainsFrontierReader"),
            "childTraceContainsFrontierReader": row.get("childTraceContainsFrontierReader"),
            "rawDescriptorTraceContainsFrontierReader": raw_reaches_reader,
            "correctedDescriptorTraceContainsFrontierReader": corrected_reaches_reader,
            "correctedDescriptorFrontierReaderStep": corrected_trace.get("frontierReaderStep"),
            "effectiveDescriptorTraceContainsFrontierReader": raw_reaches_reader or corrected_reaches_reader,
            "sourceToTargetAdjacent": sequence.get("sourceToTargetAdjacent"),
            "recordSequence": sequence.get("recordSequence") or [],
            "sourceRecordVaHex": sequence.get("sourceRecordVaHex"),
            "targetRecordVaHex": sequence.get("targetRecordVaHex"),
            "geometryExitWordHitCount": len(geometry_hits),
            "descriptorTrace": trace_brief(stream_traces, descriptor_hex),
            "childTrace": trace_brief(stream_traces, child_hex),
            "correctedDescriptorTrace": corrected_trace,
        })

    current_route_pair_raw_trace_reaches_reader = sum(
        1
        for row in rows
        if row.get("insideCurrentRootEntryRun")
        and row.get("descriptorHasRoutePair")
        and (
            row.get("descriptorTraceContainsFrontierReader")
            or row.get("childTraceContainsFrontierReader")
        )
    )
    current_route_pair_corrected_trace_reaches_reader = sum(
        1
        for row in rows
        if row.get("insideCurrentRootEntryRun")
        and row.get("descriptorHasRoutePair")
        and row.get("correctedDescriptorTraceContainsFrontierReader")
    )
    current_route_pair_trace_reaches_reader = (
        current_route_pair_corrected_trace_reaches_reader
        if corrected_by_descriptor
        else current_route_pair_raw_trace_reaches_reader
    )
    current_route_pair_geometry_hits = sum(
        row.get("geometryExitWordHitCount", 0)
        for row in rows
        if row.get("insideCurrentRootEntryRun") and row.get("descriptorHasRoutePair")
    )
    current_route_pair_adjacent = sum(
        1
        for row in rows
        if row.get("insideCurrentRootEntryRun")
        and row.get("descriptorHasRoutePair")
        and row.get("sourceToTargetAdjacent")
    )
    nested_maps = sorted({
        name
        for row in current_route_pair_rows
        for name in (row.get("descriptorNestedFieldMaps") or [])
    })
    conclusion = (
        "The current non-negative selector entries at indices 6 and 8 carry map1_01a/map2_02d "
        "route-pair scene-list descriptors. Their raw generic traces still stop at opcode 0x2c, "
        "but the opcode 0x2c correction traces both descriptors to the reader-bearing frontier "
        "0x00542b0c. This removes the stale trace contradiction while keeping the route pair as "
        "selector scene-list adjacency/control-flow evidence: the scene-record sequences still have "
        "no geometry exit word hits, the reader branch payload does not yet prove a field-map target, "
        "and strict runtime selector/hotspot proof is still missing."
    )
    return {
        "source": leaf_index.get("source"),
        "target": leaf_index.get("target"),
        "selector": leaf_index.get("selector"),
        "rootHex": leaf_index.get("rootHex"),
        "rootTablePointerHex": leaf_index.get("rootTablePointerHex"),
        "frontierReaderHex": leaf_index.get("frontierReaderHex"),
        "currentRoutePairDescriptorCount": len(current_route_pair_rows),
        "currentRoutePairDescriptorIndices": [
            row.get("rootRelativeIndex") for row in current_route_pair_rows
        ],
        "currentRoutePairDescriptorHexes": [
            row.get("descriptorHex") for row in current_route_pair_rows
        ],
        "currentRoutePairNestedFieldMaps": nested_maps,
        "currentRoutePairSceneAdjacentCount": current_route_pair_adjacent,
        "opcode2cCorrectionApplied": bool(corrected_by_descriptor),
        "currentRoutePairRawTraceReachesReaderCount": current_route_pair_raw_trace_reaches_reader,
        "currentRoutePairCorrectedTraceReachesReaderCount": current_route_pair_corrected_trace_reaches_reader,
        "currentRoutePairCorrectedTraceAllDescriptorsReachReader": bool(current_route_pair_rows)
        and current_route_pair_corrected_trace_reaches_reader == len(current_route_pair_rows),
        "currentRoutePairTraceReachesReaderCount": current_route_pair_trace_reaches_reader,
        "currentRoutePairGeometryExitHitCount": current_route_pair_geometry_hits,
        "readerBearingCurrentEntryCount": len(current_reader_rows),
        "readerBearingNegativeEntryCount": len(negative_reader_rows),
        "readerBearingNegativeIndices": [
            row.get("rootRelativeIndex") for row in negative_reader_rows
        ],
        "frontierReaderSelectableByNonNegativeIndex": leaf_index.get("frontierReaderSelectableByNonNegativeIndex"),
        "frontierReaderReachableByCorrectedNonNegativeIndex": bool(
            current_route_pair_corrected_trace_reaches_reader
        ),
        "runtimeSelectionProven": False,
        "strictHotspotFound": False,
        "proofFound": False,
        "routePairDescriptorProofFound": False,
        "failedRoutePairDescriptorGateIds": FAILED_ROUTE_PAIR_DESCRIPTOR_GATE_IDS,
        "missingEvidence": ROUTE_PAIR_DESCRIPTOR_MISSING_EVIDENCE,
        "evidenceRefs": ROUTE_PAIR_DESCRIPTOR_EVIDENCE_REFS,
        "evidenceRefCount": len(ROUTE_PAIR_DESCRIPTOR_EVIDENCE_REFS),
        "promotionStatus": "blocked",
        "rows": rows,
        "conclusion": conclusion,
        "remainingProofs": ROUTE_PAIR_DESCRIPTOR_MISSING_EVIDENCE,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Route-Pair Descriptor Context",
        "",
        f"- route: `{summary['source']} -> {summary['target']}`",
        f"- selector: `{summary['selector']}` root `{summary['rootHex']}`",
        f"- root table pointer: `{summary['rootTablePointerHex']}`",
        f"- frontier reader: `{summary['frontierReaderHex']}`",
        f"- current route-pair descriptors: {summary['currentRoutePairDescriptorCount']} at {summary['currentRoutePairDescriptorIndices']}",
        f"- current route-pair scene-adjacent descriptors: {summary['currentRoutePairSceneAdjacentCount']}",
        f"- opcode 0x2c correction applied: {summary['opcode2cCorrectionApplied']}",
        f"- current route-pair raw traces reaching reader: {summary['currentRoutePairRawTraceReachesReaderCount']}",
        f"- current route-pair corrected traces reaching reader: {summary['currentRoutePairCorrectedTraceReachesReaderCount']}",
        f"- current route-pair traces reaching reader: {summary['currentRoutePairTraceReachesReaderCount']}",
        f"- current route-pair geometry exit hits: {summary['currentRoutePairGeometryExitHitCount']}",
        f"- reader-bearing current entries: {summary['readerBearingCurrentEntryCount']}",
        f"- reader-bearing negative entries: {summary['readerBearingNegativeEntryCount']} at {summary['readerBearingNegativeIndices']}",
        f"- frontier reader selectable by non-negative index: {summary['frontierReaderSelectableByNonNegativeIndex']}",
        f"- proof found: {summary['proofFound']}",
        f"- failed route-pair descriptor gates: {', '.join(summary['failedRoutePairDescriptorGateIds'])}",
        f"- missing evidence count: {len(summary['missingEvidence'])}",
        f"- evidence refs: {summary.get('evidenceRefCount')}",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        summary["conclusion"],
        "",
        "## Route-Relevant Entries",
        "",
        "| index | entry | current | descriptor | child | desc maps | nested maps | child maps | adjacent | geo hits | raw desc trace | corrected desc trace | child trace |",
        "| ---: | --- | --- | --- | --- | --- | --- | --- | --- | ---: | --- | --- | --- |",
    ]
    for row in summary["rows"]:
        desc_trace = row.get("descriptorTrace") or {}
        child_trace = row.get("childTrace") or {}
        corrected_trace = row.get("correctedDescriptorTrace") or {}
        lines.append(
            f"| {row.get('index')} | `{row.get('entryVaHex')}` | {row.get('insideCurrentRootEntryRun')} | "
            f"`{row.get('descriptorHex')}` | `{row.get('childPointerHex') or '-'}` | "
            f"{', '.join(row.get('descriptorFieldMaps') or []) or '-'} | "
            f"{', '.join(row.get('descriptorNestedFieldMaps') or []) or '-'} | "
            f"{', '.join(row.get('childFieldMaps') or []) or '-'} | "
            f"{row.get('sourceToTargetAdjacent')} | {row.get('geometryExitWordHitCount')} | "
            f"{desc_trace.get('stepCount', '-')} steps, stop `{desc_trace.get('stopReason') or '-'}` | "
            f"{corrected_trace.get('stepCount', '-')} steps, reaches {corrected_trace.get('reachesFrontierReader', '-')} "
            f"at step {corrected_trace.get('frontierReaderStep', '-')} | "
            f"{child_trace.get('stepCount', '-')} steps, stop `{child_trace.get('stopReason') or '-'}` |"
        )
    lines.extend(["", "## Remaining Proofs", ""])
    lines.extend(f"- {item}" for item in summary["remainingProofs"])
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    rows = []
    for row in summary["rows"]:
        desc_trace = row.get("descriptorTrace") or {}
        child_trace = row.get("childTrace") or {}
        corrected_trace = row.get("correctedDescriptorTrace") or {}
        rows.append(
            "<tr>"
            f"<td>{row.get('index')}</td>"
            f"<td><code>{html.escape(str(row.get('entryVaHex')))}</code></td>"
            f"<td>{row.get('insideCurrentRootEntryRun')}</td>"
            f"<td><code>{html.escape(str(row.get('descriptorHex')))}</code></td>"
            f"<td><code>{html.escape(str(row.get('childPointerHex') or '-'))}</code></td>"
            f"<td>{html.escape(', '.join(row.get('descriptorFieldMaps') or []) or '-')}</td>"
            f"<td>{html.escape(', '.join(row.get('descriptorNestedFieldMaps') or []) or '-')}</td>"
            f"<td>{html.escape(', '.join(row.get('childFieldMaps') or []) or '-')}</td>"
            f"<td>{row.get('sourceToTargetAdjacent')}</td>"
            f"<td>{row.get('geometryExitWordHitCount')}</td>"
            f"<td>{desc_trace.get('stepCount', '-')} steps, stop <code>{html.escape(str(desc_trace.get('stopReason') or '-'))}</code></td>"
            f"<td>{corrected_trace.get('stepCount', '-')} steps, reaches {corrected_trace.get('reachesFrontierReader', '-')}, reader step {corrected_trace.get('frontierReaderStep', '-')}</td>"
            f"<td>{child_trace.get('stepCount', '-')} steps, stop <code>{html.escape(str(child_trace.get('stopReason') or '-'))}</code></td>"
            "</tr>"
        )
    proofs = "".join(f"<li>{html.escape(item)}</li>" for item in summary["remainingProofs"])
    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 Route-Pair Descriptor Context</title>",
        "  <style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;margin:24px}table{border-collapse:collapse;width:100%;max-width:1280px}td,th{border:1px solid #333;padding:6px 8px;text-align:left;vertical-align:top}th{background:#1f1f1f}code{color:#9bd4ff}</style>",
        "</head>",
        "<body>",
        "  <h1>Save Selector Route-Pair Descriptor Context</h1>",
        f"  <p>route <code>{html.escape(str(summary['source']))} -&gt; {html.escape(str(summary['target']))}</code>; selector <code>{html.escape(str(summary['selector']))}</code>; current route-pair descriptors {summary['currentRoutePairDescriptorCount']}; opcode 0x2c correction applied {summary['opcode2cCorrectionApplied']}; current route-pair raw traces reaching reader {summary['currentRoutePairRawTraceReachesReaderCount']}; current route-pair corrected traces reaching reader {summary['currentRoutePairCorrectedTraceReachesReaderCount']}; current route-pair traces reaching reader {summary['currentRoutePairTraceReachesReaderCount']}; current route-pair geometry exit hits {summary['currentRoutePairGeometryExitHitCount']}; reader-bearing negative entries {summary['readerBearingNegativeEntryCount']} at {summary['readerBearingNegativeIndices']}; promotion <code>{html.escape(str(summary['promotionStatus']))}</code>.</p>",
        f"  <p>proof found <code>{summary['proofFound']}</code>; failed route-pair descriptor gates <code>{html.escape(','.join(summary['failedRoutePairDescriptorGateIds']))}</code>; missing evidence <code>{len(summary['missingEvidence'])}</code>; evidence refs <code>{summary.get('evidenceRefCount')}</code>.</p>",
        f"  <p>{html.escape(summary['conclusion'])}</p>",
        "  <table><thead><tr><th>index</th><th>entry</th><th>current</th><th>descriptor</th><th>child</th><th>desc maps</th><th>nested maps</th><th>child maps</th><th>adjacent</th><th>geo hits</th><th>raw desc trace</th><th>corrected desc trace</th><th>child trace</th></tr></thead>",
        f"  <tbody>{''.join(rows)}</tbody></table>",
        "  <h2>Remaining Proofs</h2>",
        f"  <ul>{proofs}</ul>",
        "</body>",
        "</html>",
        "",
    ])


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_route_pair_descriptor_context.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()
    out_dir = args.out_dir
    summary = build_summary(
        load_json(out_dir / "save_selector_leaf_index_space.json", {}),
        load_json(out_dir / "save_selector_scene_record_sequence.json", {}),
        load_json(out_dir / "save_selector_stream_traces.json", []),
        load_json(out_dir / "save_selector_opcode2c_route_pair_context.json", {}),
    )
    json_out = write_outputs(summary, out_dir, args.html_out)
    print(f"wrote route-pair descriptor context -> {json_out}")


if __name__ == "__main__":
    main()
