#!/usr/bin/env python3
"""Summarize the remaining persistence gap from selector 1:0 to 2:0."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
PREDECESSOR_SELECTOR = "1:0"
CURRENT_SELECTOR = "2:0"

FAILED_PREDECESSOR_PERSISTENCE_GATE_IDS = [
    "source-target-selector-merge-order",
    "global-vm-helper-reset-exclusion",
    "strict-source-hotspot",
]
PREDECESSOR_PERSISTENCE_MISSING_EVIDENCE = [
    "source-side selector 0:0 progressing into selector 2:0 after target-side 1:0 state is established",
    "proof that no global VM/helper reset clears secondaryBranchState between predecessor fill and current reader",
    "strict map1_01a source coordinate or hotspot",
]
PREDECESSOR_PERSISTENCE_EVIDENCE_REFS = [
    {
        "path": "out/save_scene_selectors.json",
        "fields": ["selectors", "fieldMaps", "selectedPointerHex"],
    },
    {
        "path": "out/save_selector_predecessor_state_effect.json",
        "fields": ["predecessorFillVas", "fillValueHex", "allStartsPassReader"],
    },
    {
        "path": "out/save_selector_predecessor_route_order.json",
        "fields": [
            "routeOrderProven",
            "proofFound",
            "predecessorRouteOrderProofFound",
            "failedPredecessorRouteOrderGateIds",
            "missingEvidence",
            "evidenceRefs",
            "evidenceRefCount",
            "selectorMergeGapOpen",
            "sourceRoutePreviousSelector",
            "predecessorIsTargetSideOnly",
        ],
    },
    {
        "path": "out/save_selector_secondary_state_sources.json",
        "fields": [
            "validBeforeFrontierCount",
            "validBeforeFrontierNonOperandCount",
            "secondaryDirectWrites",
        ],
    },
    {
        "path": "out/save_selector_current_state_sources.json",
        "fields": [
            "validActivationCandidateCount",
            "validBeforeFirstFrontierReaderWithExecutionEvidenceCount",
        ],
    },
    {
        "path": "out/map1_01a_hotspot_gap.json",
        "fields": [
            "strictHotspotFound",
            "eventTransitionCount",
            "manifestPointPromotableSourceCount",
        ],
    },
]


def selector_key(row: dict) -> str:
    return f"{row.get('group')}:{row.get('slot')}"


def selector_sort_key(row: dict) -> tuple[int, int, int]:
    return (
        int(row.get("group") or 0),
        int(row.get("slot") or 0),
        int(row.get("_inputIndex") or 0),
    )


def ordered_selectors(selectors: list[dict]) -> list[dict]:
    rows = []
    for index, row in enumerate(selectors):
        if row.get("fieldMaps"):
            rows.append({**row, "_inputIndex": index})
    return sorted(rows, key=selector_sort_key)


def find_selector(rows: list[dict], key: str) -> tuple[int, dict]:
    for index, row in enumerate(rows):
        if selector_key(row) == key:
            return index, row
    raise ValueError(f"selector {key} not found")


def build_summary(
    selectors: list[dict],
    inherited: dict,
    predecessor_effect: dict,
    route_order: dict,
    secondary_sources: dict,
    current_state_sources: dict,
    hotspot_gap: dict,
) -> dict:
    rows = ordered_selectors(selectors)
    predecessor_index, predecessor_row = find_selector(rows, PREDECESSOR_SELECTOR)
    current_index, current_row = find_selector(rows, CURRENT_SELECTOR)
    if predecessor_index >= current_index:
        raise ValueError("predecessor selector must appear before current selector")
    intermediate = rows[predecessor_index + 1:current_index]
    secondary_direct = secondary_sources.get("secondaryDirectWrites") or {}
    adjacent = current_index == predecessor_index + 1
    current_no_known_overwrite = (
        secondary_sources.get("validBeforeFrontierCount") == 0
        and secondary_sources.get("validBeforeFrontierNonOperandCount") == 0
        and secondary_direct.get("directWriterCount") == 0
        and current_state_sources.get("validActivationCandidateCount") == 0
        and current_state_sources.get("validBeforeFirstFrontierReaderWithExecutionEvidenceCount") == 0
    )
    predecessor_fill_would_pass = predecessor_effect.get("allStartsPassReader") is True
    route_order_proven = route_order.get("routeOrderProven") is True
    strict_hotspot_found = hotspot_gap.get("strictHotspotFound") is True
    selector_merge_gap_open = route_order.get("selectorMergeGapOpen") is True
    predecessor_is_target_side_only = route_order.get("predecessorIsTargetSideOnly") is True
    persistence_proven = (
        adjacent
        and current_no_known_overwrite
        and predecessor_fill_would_pass
        and route_order_proven
        and strict_hotspot_found
    )
    evidence = [
        {
            "kind": "selector-index-adjacency",
            "status": "supports-persistence-candidate" if adjacent else "has-intermediate-selectors",
            "detail": (
                f"{PREDECESSOR_SELECTOR} index {predecessor_index} is immediately before {CURRENT_SELECTOR} "
                f"index {current_index}; intermediate selectors={len(intermediate)}."
            ),
        },
        {
            "kind": "predecessor-fill-effect",
            "status": "would-pass-current-reader" if predecessor_fill_would_pass else "does-not-pass-current-reader",
            "detail": (
                f"{', '.join(predecessor_effect.get('predecessorFillVas') or [])} "
                f"{predecessor_effect.get('fillValueHex')} -> all starts pass reader="
                f"{predecessor_effect.get('allStartsPassReader')}."
            ),
        },
        {
            "kind": "current-root-overwrite",
            "status": "no-known-local-overwrite" if current_no_known_overwrite else "possible-local-overwrite",
            "detail": (
                f"valid before-frontier secondary fills={secondary_sources.get('validBeforeFrontierCount')}; "
                f"valid activation fills={current_state_sources.get('validActivationCandidateCount')}; "
                f"direct secondaryBranchState writers={secondary_direct.get('directWriterCount')}."
            ),
        },
        {
            "kind": "confirmed-route-order",
            "status": "proven" if route_order_proven else "does-not-prove-runtime-order",
            "detail": (
                f"confirmed overlap with {PREDECESSOR_SELECTOR}="
                f"{', '.join(route_order.get('predecessorConfirmedOverlap') or []) or 'none'}; "
                f"routeOrderProven={route_order.get('routeOrderProven')}."
            ),
        },
        {
            "kind": "selector-merge-shape",
            "status": "merge-gap-open" if selector_merge_gap_open else "not-merge-shaped",
            "detail": (
                f"source-side previous={route_order.get('sourceRoutePreviousSelector')}; "
                f"source confirmed overlap={', '.join(route_order.get('sourceRoutePreviousConfirmedOverlap') or []) or 'none'}; "
                f"predecessor target-side only={predecessor_is_target_side_only}; "
                f"same previous contains route pair={route_order.get('samePreviousContainsRoutePair')}."
            ),
        },
        {
            "kind": "strict-hotspot",
            "status": "found" if strict_hotspot_found else "missing",
            "detail": (
                f"strictHotspotFound={hotspot_gap.get('strictHotspotFound')}; "
                f"event transitions={hotspot_gap.get('eventTransitionCount')}; "
                f"manifest source point tables={hotspot_gap.get('manifestPointPromotableSourceCount')}."
            ),
        },
    ]
    conclusion = (
        "Selector table order now supports a narrow persistence candidate: 1:0 is immediately before 2:0, "
        "there are no intermediate field-map selectors, the current 2:0 root has no known valid secondaryBranchState "
        "fill before 0x00542b0c, and the predecessor fill would satisfy the current reader. The blocker is now "
        "narrower but sharper: 1:0 is target-side only while the confirmed source-side route overlaps 0:0, so 2:0 "
        "looks like a selector-merge state. This still does not promote map1_01a->map2_02d because selector-index "
        "adjacency is not runtime execution proof, the source-side/target-side merge order is unproven, and no "
        "strict map1_01a source hotspot has been found."
    )
    return {
        "source": "map1_01a",
        "target": "map2_02d",
        "predecessorSelector": PREDECESSOR_SELECTOR,
        "predecessorRootHex": predecessor_row.get("selectedPointerHex"),
        "predecessorIndex": predecessor_index,
        "currentSelector": CURRENT_SELECTOR,
        "currentRootHex": current_row.get("selectedPointerHex"),
        "currentIndex": current_index,
        "selectorAdjacent": adjacent,
        "intermediateSelectorCount": len(intermediate),
        "intermediateSelectors": [
            {
                "selector": selector_key(row),
                "rootHex": row.get("selectedPointerHex"),
                "fieldMaps": row.get("fieldMaps") or [],
            }
            for row in intermediate
        ],
        "bestPreviousSelector": (inherited.get("bestPrevious") or {}).get("selector"),
        "predecessorFillWouldPassCurrentReader": predecessor_fill_would_pass,
        "currentRootHasNoKnownBeforeFrontierOverwrite": current_no_known_overwrite,
        "currentValidBeforeFrontierSecondaryFillCount": secondary_sources.get("validBeforeFrontierCount"),
        "currentValidActivationFillCount": current_state_sources.get("validActivationCandidateCount"),
        "currentValidExecutionEvidenceFillCount": current_state_sources.get(
            "validBeforeFirstFrontierReaderWithExecutionEvidenceCount"
        ),
        "directSecondaryBranchStateWriterCount": secondary_direct.get("directWriterCount"),
        "routeOrderProven": route_order_proven,
        "sourceRoutePreviousSelector": route_order.get("sourceRoutePreviousSelector"),
        "sourceRoutePreviousRootHex": route_order.get("sourceRoutePreviousRootHex"),
        "sourceRoutePreviousConfirmedOverlap": route_order.get("sourceRoutePreviousConfirmedOverlap") or [],
        "predecessorIsTargetSideOnly": predecessor_is_target_side_only,
        "samePreviousContainsRoutePair": route_order.get("samePreviousContainsRoutePair"),
        "selectorMergeGapOpen": selector_merge_gap_open,
        "strictHotspotFound": strict_hotspot_found,
        "persistenceProven": persistence_proven,
        "proofFound": persistence_proven,
        "predecessorPersistenceProofFound": persistence_proven,
        "failedPredecessorPersistenceGateIds": (
            [] if persistence_proven else FAILED_PREDECESSOR_PERSISTENCE_GATE_IDS
        ),
        "missingEvidence": [] if persistence_proven else PREDECESSOR_PERSISTENCE_MISSING_EVIDENCE,
        "evidenceRefs": PREDECESSOR_PERSISTENCE_EVIDENCE_REFS,
        "evidenceRefCount": len(PREDECESSOR_PERSISTENCE_EVIDENCE_REFS),
        "promotionStatus": "blocked",
        "remainingProofs": PREDECESSOR_PERSISTENCE_MISSING_EVIDENCE,
        "evidence": evidence,
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Predecessor Persistence Gap",
        "",
        f"- predecessor: `{summary['predecessorSelector']}` root `{summary['predecessorRootHex']}` index {summary['predecessorIndex']}",
        f"- current: `{summary['currentSelector']}` root `{summary['currentRootHex']}` index {summary['currentIndex']}",
        f"- selector adjacent: {summary['selectorAdjacent']}",
        f"- intermediate selectors: {summary['intermediateSelectorCount']}",
        f"- source-side previous selector: `{summary.get('sourceRoutePreviousSelector')}` overlap {', '.join(summary.get('sourceRoutePreviousConfirmedOverlap') or []) or 'none'}",
        f"- predecessor target-side only: {summary.get('predecessorIsTargetSideOnly')}",
        f"- selector merge gap open: {summary.get('selectorMergeGapOpen')}",
        f"- predecessor fill would pass current reader: {summary['predecessorFillWouldPassCurrentReader']}",
        f"- current root has no known before-frontier overwrite: {summary['currentRootHasNoKnownBeforeFrontierOverwrite']}",
        f"- direct secondaryBranchState writers: {summary['directSecondaryBranchStateWriterCount']}",
        f"- route order proven: {summary['routeOrderProven']}",
        f"- strict hotspot found: {summary['strictHotspotFound']}",
        f"- persistence proven: {summary['persistenceProven']}",
        f"- proof found: {summary['proofFound']}",
        f"- predecessor persistence proof found: {summary['predecessorPersistenceProofFound']}",
        f"- failed predecessor persistence gates: `{','.join(summary['failedPredecessorPersistenceGateIds'])}`",
        f"- missing evidence count: {len(summary['missingEvidence'])}",
        f"- evidence refs: {summary['evidenceRefCount']}",
        f"- promotion status: {summary['promotionStatus']}",
        "",
        summary["conclusion"],
        "",
        "## Missing Evidence",
        "",
        *[f"- {item}" for item in summary["missingEvidence"]],
        "",
        "## Evidence Refs",
        "",
        "| path | fields |",
        "| --- | --- |",
        *[
            f"| `{row['path']}` | {', '.join(row.get('fields') or []) or '-'} |"
            for row in summary["evidenceRefs"]
        ],
        "",
        "## Evidence",
        "",
        "| kind | status | detail |",
        "| --- | --- | --- |",
    ]
    for row in summary["evidence"]:
        lines.append(f"| {row['kind']} | {row['status']} | {row['detail']} |")
    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:
    evidence_rows = "\n".join(
        "<tr>"
        f"<td>{html.escape(row['kind'])}</td>"
        f"<td>{html.escape(row['status'])}</td>"
        f"<td>{html.escape(row['detail'])}</td>"
        "</tr>"
        for row in summary["evidence"]
    )
    proofs = "".join(f"<li>{html.escape(item)}</li>" for item in summary["remainingProofs"])
    missing = "".join(f"<li>{html.escape(item)}</li>" for item in summary["missingEvidence"])
    refs = "\n".join(
        "<tr>"
        f"<td><code>{html.escape(row['path'])}</code></td>"
        f"<td>{html.escape(', '.join(row.get('fields') or []) or '-')}</td>"
        "</tr>"
        for row in summary["evidenceRefs"]
    )
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Predecessor Persistence Gap</title>",
        "<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;max-width:1100px;margin:24px auto}table{border-collapse:collapse}td,th{border:1px solid #444;padding:6px 8px;vertical-align:top}code{color:#9bd4ff}</style>",
        "<h1>Save Selector Predecessor Persistence Gap</h1>",
        f"<p>Predecessor <code>{summary['predecessorSelector']}</code> root <code>{summary['predecessorRootHex']}</code> index {summary['predecessorIndex']}; current <code>{summary['currentSelector']}</code> root <code>{summary['currentRootHex']}</code> index {summary['currentIndex']}.</p>",
        f"<p>selector adjacent: {summary['selectorAdjacent']}; intermediate selectors: {summary['intermediateSelectorCount']}; persistence proven: {summary['persistenceProven']}; promotion status: {html.escape(summary['promotionStatus'])}</p>",
        (
            f"<p>proof found: {summary['proofFound']}; "
            f"predecessor persistence proof found: {summary['predecessorPersistenceProofFound']}; "
            "failed predecessor persistence gates: "
            f"<code>{html.escape(','.join(summary['failedPredecessorPersistenceGateIds']))}</code>; "
            f"missing evidence count: {len(summary['missingEvidence'])}; "
            f"evidence refs: {summary['evidenceRefCount']}.</p>"
        ),
        f"<p>source-side previous selector: <code>{html.escape(str(summary.get('sourceRoutePreviousSelector')))}</code>; overlap: {html.escape(', '.join(summary.get('sourceRoutePreviousConfirmedOverlap') or []) or 'none')}; predecessor target-side only: {summary.get('predecessorIsTargetSideOnly')}; selector merge gap open: {summary.get('selectorMergeGapOpen')}</p>",
        f"<p>current root has no known before-frontier overwrite: {summary['currentRootHasNoKnownBeforeFrontierOverwrite']}; direct secondaryBranchState writers: {summary['directSecondaryBranchStateWriterCount']}</p>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<h2>Missing Evidence</h2>",
        f"<ul>{missing}</ul>",
        "<h2>Evidence Refs</h2>",
        f"<table><thead><tr><th>path</th><th>fields</th></tr></thead><tbody>{refs}</tbody></table>",
        "<table><thead><tr><th>kind</th><th>status</th><th>detail</th></tr></thead><tbody>",
        evidence_rows,
        "</tbody></table>",
        "<h2>Remaining Proofs</h2>",
        f"<ul>{proofs}</ul>",
    ])


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


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--selectors", type=Path, default=OUT / "save_scene_selectors.json")
    parser.add_argument("--inherited", type=Path, default=OUT / "save_selector_inherited_state_candidates.json")
    parser.add_argument("--predecessor-effect", type=Path, default=OUT / "save_selector_predecessor_state_effect.json")
    parser.add_argument("--route-order", type=Path, default=OUT / "save_selector_predecessor_route_order.json")
    parser.add_argument("--secondary-sources", type=Path, default=OUT / "save_selector_secondary_state_sources.json")
    parser.add_argument("--current-state-sources", type=Path, default=OUT / "save_selector_current_state_sources.json")
    parser.add_argument("--hotspot-gap", type=Path, default=OUT / "map1_01a_hotspot_gap.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_summary(
        json.loads(args.selectors.read_text(encoding="utf-8")),
        json.loads(args.inherited.read_text(encoding="utf-8")),
        json.loads(args.predecessor_effect.read_text(encoding="utf-8")),
        json.loads(args.route_order.read_text(encoding="utf-8")),
        json.loads(args.secondary_sources.read_text(encoding="utf-8")),
        json.loads(args.current_state_sources.read_text(encoding="utf-8")),
        json.loads(args.hotspot_gap.read_text(encoding="utf-8")),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote predecessor persistence gap -> {args.out_dir / 'save_selector_predecessor_persistence_gap.html'}")


if __name__ == "__main__":
    main()
