#!/usr/bin/env python3
"""Consolidate the selector-merge evidence for the map1_01a 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"
SOURCE_SELECTOR = "0:0"
TARGET_SELECTOR = "1:0"
CURRENT_SELECTOR = "2:0"


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


def selector_maps(selectors: list[dict], key: str) -> list[str]:
    group_text, slot_text = key.split(":", 1)
    group = int(group_text)
    slot = int(slot_text)
    for row in selectors:
        if row.get("group") == group and row.get("slot") == slot:
            return row.get("fieldMaps") or []
    return []


def progress_row(progress: dict, key: str) -> dict:
    group_text, slot_text = key.split(":", 1)
    group = int(group_text)
    slot = int(slot_text)
    for row in progress.get("progressRows") or []:
        if row.get("group") == group and row.get("slot") == slot:
            return row
    return {}


def build_summary(
    selectors: list[dict],
    scene_selector_progress: dict,
    map1_selector_bridge_refs: dict,
    predecessor_bridge_refs: dict,
    savedata_sample_coverage: dict,
    predecessor_persistence_gap: dict,
) -> dict:
    source_maps = selector_maps(selectors, SOURCE_SELECTOR)
    target_maps = selector_maps(selectors, TARGET_SELECTOR)
    current_maps = selector_maps(selectors, CURRENT_SELECTOR)
    current_progress = progress_row(scene_selector_progress, CURRENT_SELECTOR)
    previous_candidates = current_progress.get("previousCandidates") or []
    source_candidate = next((row for row in previous_candidates if row.get("fromLabel") == SOURCE_SELECTOR), {})
    target_candidate = next((row for row in previous_candidates if row.get("fromLabel") == TARGET_SELECTOR), {})
    same_previous_contains_pair = any(
        SOURCE in (row.get("fromFieldMaps") or []) and TARGET in (row.get("fromFieldMaps") or [])
        for row in previous_candidates
    )
    source_bridge = map1_selector_bridge_refs.get("confirmedToFrontier") or {}
    frontier_bridge = map1_selector_bridge_refs.get("frontierToConfirmed") or {}
    predecessor_to_current = predecessor_bridge_refs.get("predecessorToCurrent") or {}
    current_to_predecessor = predecessor_bridge_refs.get("currentToPredecessor") or {}
    selectors_seen = savedata_sample_coverage.get("sampleSelectors") or []
    current_sample_covered = savedata_sample_coverage.get("currentFrontierSampleCovered") is True
    sample_route_pair_covered = savedata_sample_coverage.get("routePairSampleCovered") is True
    merge_gap_open = (
        SOURCE in source_maps
        and TARGET not in source_maps
        and TARGET in target_maps
        and SOURCE not in target_maps
        and {SOURCE, TARGET}.issubset(set(current_maps))
        and not same_previous_contains_pair
        and (source_bridge.get("dwordHitCount") or 0) == 0
        and (frontier_bridge.get("dwordHitCount") or 0) == 0
        and (predecessor_to_current.get("hitCount") or 0) == 0
        and predecessor_bridge_refs.get("reverseHitsOnlyBeforeFill") is True
        and not current_sample_covered
    )
    evidence = [
        {
            "kind": "source-side-selector",
            "status": "source-only" if SOURCE in source_maps and TARGET not in source_maps else "mixed",
            "detail": f"{SOURCE_SELECTOR} maps={', '.join(source_maps) or '-'}",
        },
        {
            "kind": "target-side-selector",
            "status": "target-only" if TARGET in target_maps and SOURCE not in target_maps else "mixed",
            "detail": f"{TARGET_SELECTOR} maps={', '.join(target_maps) or '-'}",
        },
        {
            "kind": "current-selector",
            "status": "contains-route-pair" if {SOURCE, TARGET}.issubset(set(current_maps)) else "missing-route-map",
            "detail": f"{CURRENT_SELECTOR} maps include {SOURCE}={SOURCE in current_maps}, {TARGET}={TARGET in current_maps}",
        },
        {
            "kind": "progress-candidates",
            "status": "split-source-target",
            "detail": (
                f"best previous={target_candidate.get('fromLabel')}; "
                f"source previous={source_candidate.get('fromLabel')}; "
                f"same previous contains route pair={same_previous_contains_pair}."
            ),
        },
        {
            "kind": "source-current-bridge",
            "status": "no-direct-bridge",
            "detail": (
                f"{SOURCE_SELECTOR}->2:0 hits={source_bridge.get('dwordHitCount')}; "
                f"2:0->{SOURCE_SELECTOR} hits={frontier_bridge.get('dwordHitCount')}."
            ),
        },
        {
            "kind": "target-current-bridge",
            "status": "reuse-only-before-fill",
            "detail": (
                f"{TARGET_SELECTOR}->2:0 hits={predecessor_to_current.get('hitCount')}; "
                f"2:0->{TARGET_SELECTOR} hits={current_to_predecessor.get('hitCount')}; "
                f"reverse hits before fill={predecessor_bridge_refs.get('reverseHitsBeforeFillCount')}."
            ),
        },
        {
            "kind": "public-savedata-samples",
            "status": "no-current-frontier-sample" if not current_sample_covered else "covered",
            "detail": (
                f"sample selectors={', '.join(selectors_seen) or '-'}; "
                f"current covered={current_sample_covered}; route pair covered={sample_route_pair_covered}."
            ),
        },
        {
            "kind": "persistence",
            "status": "blocked",
            "detail": (
                f"wouldPass={predecessor_persistence_gap.get('predecessorFillWouldPassCurrentReader')}; "
                f"routeOrder={predecessor_persistence_gap.get('routeOrderProven')}; "
                f"selectorMergeGap={predecessor_persistence_gap.get('selectorMergeGapOpen')}."
            ),
        },
    ]
    conclusion = (
        "The current selector 2:0 contains both the confirmed source-side map1_01a and the target-side map2_02d list, "
        "but the evidence remains a split selector merge rather than a proven transition. Selector 0:0 carries the "
        "confirmed source route without map2_02d; selector 1:0 carries map2_02d without map1_01a. There is no direct "
        "0:0<->2:0 bridge, no forward 1:0->2:0 bridge, and the 2:0->1:0 reverse hits only point before the secondary "
        "fill sites. Public savedata samples do not cover selector 2:0. Promotion therefore still needs a selector 2:0 "
        "save/runtime trace or a strict map1_01a hotspot."
    )
    return {
        "source": SOURCE,
        "target": TARGET,
        "sourceSelector": SOURCE_SELECTOR,
        "targetSelector": TARGET_SELECTOR,
        "currentSelector": CURRENT_SELECTOR,
        "sourceSelectorMaps": source_maps,
        "targetSelectorMaps": target_maps,
        "currentSelectorMaps": current_maps,
        "sourceCandidateSharedMaps": source_candidate.get("sharedMaps") or [],
        "targetCandidateSharedMaps": target_candidate.get("sharedMaps") or [],
        "samePreviousContainsRoutePair": same_previous_contains_pair,
        "sourceToCurrentBridgeHitCount": source_bridge.get("dwordHitCount", 0),
        "currentToSourceBridgeHitCount": frontier_bridge.get("dwordHitCount", 0),
        "targetToCurrentBridgeHitCount": predecessor_to_current.get("hitCount", 0),
        "currentToTargetBridgeHitCount": current_to_predecessor.get("hitCount", 0),
        "targetReverseHitsOnlyBeforeFill": predecessor_bridge_refs.get("reverseHitsOnlyBeforeFill"),
        "targetReverseHitsToFillSiteCount": predecessor_bridge_refs.get("reverseHitsToFillSiteCount"),
        "currentFrontierSampleCovered": current_sample_covered,
        "routePairSampleCovered": sample_route_pair_covered,
        "sampleSelectors": selectors_seen,
        "predecessorPersistenceWouldPass": predecessor_persistence_gap.get("predecessorFillWouldPassCurrentReader"),
        "predecessorRouteOrderProven": predecessor_persistence_gap.get("routeOrderProven"),
        "predecessorPersistenceProven": predecessor_persistence_gap.get("persistenceProven"),
        "selectorMergeGapOpen": merge_gap_open,
        "promotionStatus": "blocked",
        "evidence": evidence,
        "remainingProofs": [
            "capture or construct a savedat/runtime trace whose scene selector is 2:0",
            "prove the 0:0 source-side route can enter 2:0 after the 1:0 target-side secondary state is established",
            "find a strict map1_01a source coordinate, hotspot, or equivalent runtime trigger",
        ],
        "conclusion": conclusion,
    }


def markdown(summary: dict) -> str:
    lines = [
        "# Save Selector Merge Gap",
        "",
        f"- route: {summary['source']} -> {summary['target']}",
        f"- source-side selector: `{summary['sourceSelector']}`",
        f"- target-side selector: `{summary['targetSelector']}`",
        f"- current selector: `{summary['currentSelector']}`",
        f"- same previous contains route pair: {summary['samePreviousContainsRoutePair']}",
        f"- source/current bridge hits: {summary['sourceToCurrentBridgeHitCount']} / {summary['currentToSourceBridgeHitCount']}",
        f"- target/current bridge hits: {summary['targetToCurrentBridgeHitCount']} / {summary['currentToTargetBridgeHitCount']}",
        f"- target reverse hits only before fill: {summary['targetReverseHitsOnlyBeforeFill']}",
        f"- current frontier sample covered: {summary['currentFrontierSampleCovered']}",
        f"- selector merge gap open: {summary['selectorMergeGapOpen']}",
        f"- promotion status: {summary['promotionStatus']}",
        "",
        summary["conclusion"],
        "",
        "## 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"])
    return "\n".join([
        "<!doctype html><meta charset=\"utf-8\"><title>Save Selector Merge Gap</title>",
        "<style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;max-width:1200px;margin:24px auto}table{border-collapse:collapse;width:100%}td,th{border:1px solid #444;padding:6px 8px;vertical-align:top}code{color:#9bd4ff}</style>",
        "<h1>Save Selector Merge Gap</h1>",
        f"<p>Route: {html.escape(summary['source'])} -&gt; {html.escape(summary['target'])}; source-side selector <code>{summary['sourceSelector']}</code>; target-side selector <code>{summary['targetSelector']}</code>; current selector <code>{summary['currentSelector']}</code>.</p>",
        f"<p>selector merge gap open: {summary['selectorMergeGapOpen']}; promotion status: <code>{summary['promotionStatus']}</code></p>",
        f"<p>{html.escape(summary['conclusion'])}</p>",
        "<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_merge_gap.json").write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--selectors", type=Path, default=OUT / "save_scene_selectors.json")
    parser.add_argument("--scene-selector-progress", type=Path, default=OUT / "scene_selector_progress.json")
    parser.add_argument("--source-bridge", type=Path, default=OUT / "map1_01a_selector_bridge_refs.json")
    parser.add_argument("--target-bridge", type=Path, default=OUT / "save_selector_predecessor_bridge_refs.json")
    parser.add_argument("--savedata-samples", type=Path, default=OUT / "savedata_sample_coverage.json")
    parser.add_argument("--persistence-gap", type=Path, default=OUT / "save_selector_predecessor_persistence_gap.json")
    parser.add_argument("--out-dir", type=Path, default=OUT)
    args = parser.parse_args()
    summary = build_summary(
        load_json(args.selectors, []),
        load_json(args.scene_selector_progress, {}),
        load_json(args.source_bridge, {}),
        load_json(args.target_bridge, {}),
        load_json(args.savedata_samples, {}),
        load_json(args.persistence_gap, {}),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote save selector merge gap -> {args.out_dir / 'save_selector_merge_gap.json'}")


if __name__ == "__main__":
    main()
