#!/usr/bin/env python3
"""Summarize predecessor-save runtime route attempts."""
from __future__ import annotations

import argparse
import html
import json
from collections import Counter
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
PREFIX = "runtime_selected_pointer_predecessor_"
SUFFIX = "_poll.json"
MAPSET_ALIASES = OUT / "save_selector_mapset_aliases.json"
SCENE_ADJACENCY_INDEX = OUT / "save_selector_scene_adjacency_index.json"
GLOBAL_SELECTED_POINTER_PATHS = OUT / "save_selector_global_selected_pointer_paths.json"
SAVE_SCENE_SELECTORS = OUT / "save_scene_selectors.json"
SAVE_SCENE_SELECTOR_REFS = OUT / "save_scene_selector_references.json"
PUBLIC_PREDECESSOR_SELECTOR = "1:0"
TARGET_SELECTOR = "2:0"
CURRENT_ROOT_HEX = "0x00540714"
SOURCE_MAP = "map1_01a"
TARGET_MAP = "map2_02d"
EVIDENCE_REFS = [
    {
        "path": "out/runtime_selected_pointer_predecessor_*poll.json",
        "fields": [
            "uniqueSelectorContexts",
            "routeSelectorHitCount",
            "currentRootHitCount",
            "uniqueWatchValues",
        ],
    },
    {
        "path": "out/save_selector_mapset_aliases.json",
        "fields": [
            "duplicateGroups",
            "targetAliasGroup",
            "aliases",
            "fieldMaps",
        ],
    },
    {
        "path": "out/save_selector_scene_adjacency_index.json",
        "fields": [
            "pairRows",
            "selectors",
            "strictEventBacked",
            "confirmedReviewBacked",
            "selectorAdjacencyOnly",
        ],
    },
    {
        "path": "out/save_selector_global_selected_pointer_paths.json",
        "fields": [
            "selectorRootScans",
            "selectedRootExecutionRefFound",
            "opcode07SelectsCurrentRootCount",
            "opcode09StoresCurrentRootCount",
        ],
    },
    {
        "path": "out/save_scene_selectors.json",
        "fields": [
            "group",
            "slot",
            "selectedPointerHex",
            "fieldMaps",
        ],
    },
    {
        "path": "out/save_scene_selector_references.json",
        "fields": [
            "label",
            "resource",
            "filename",
            "sceneMatched",
            "pathHex",
        ],
    },
]


def load_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8"))


def load_optional_json(path: Path) -> Any:
    return json.loads(path.read_text(encoding="utf-8")) if path.exists() else {}


def short_label(path: Path) -> str:
    name = path.name
    if name.startswith(PREFIX):
        name = name[len(PREFIX):]
    if name.endswith(SUFFIX):
        name = name[: -len(SUFFIX)]
    return name.replace("_", "-")


def selector_counts(rows: list[dict[str, Any]]) -> Counter[str]:
    counts: Counter[str] = Counter()
    for row in rows:
        for item in row.get("uniqueSelectorContexts") or []:
            selector = item.get("selector")
            if selector:
                counts[selector] += int(item.get("count") or 0)
    return counts


def watch_value_counts(rows: list[dict[str, Any]], prefix: str) -> dict[str, int]:
    counts: Counter[str] = Counter()
    for row in rows:
        unique = row.get("uniqueWatchValues") or {}
        for key, values in unique.items():
            if not key.startswith(prefix):
                continue
            for value in values or []:
                value_hex = value.get("valueHex")
                if value_hex:
                    counts[f"{key}={value_hex}"] += int(value.get("count") or 0)
    return dict(sorted(counts.items()))


def selector_matches(values: list[Any] | None, selector: str | None) -> bool:
    if not selector:
        return False
    return any(str(value) == selector for value in values or [])


def selector_context_rows(files: list[dict[str, Any]], selector: str) -> list[dict[str, Any]]:
    rows: list[dict[str, Any]] = []
    for item in files:
        count = int((item.get("selectorCounts") or {}).get(selector) or 0)
        if not count:
            continue
        rows.append({
            "path": item.get("path"),
            "label": item.get("label"),
            "sampleCount": count,
            "selectorShareOfFile": count / int(item.get("sampleCount") or 1),
            "routeSelectorHitCount": item.get("routeSelectorHitCount"),
            "currentRootHitCount": item.get("currentRootHitCount"),
        })
    return rows


def find_alias_group(mapset_aliases: dict[str, Any], selector: str | None) -> dict[str, Any]:
    if not selector:
        return {}
    groups = list(mapset_aliases.get("duplicateGroups") or [])
    target_group = mapset_aliases.get("targetAliasGroup") or {}
    if target_group:
        groups.append(target_group)
    for group in groups:
        for alias in group.get("aliases") or []:
            if isinstance(alias, dict) and alias.get("selector") == selector:
                return group
    return {}


def alias_row(alias_group: dict[str, Any], selector: str | None) -> dict[str, Any]:
    for alias in alias_group.get("aliases") or []:
        if isinstance(alias, dict) and alias.get("selector") == selector:
            return alias
    return {}


def scene_adjacency_rows(scene_adjacency: dict[str, Any], selector: str | None) -> list[dict[str, Any]]:
    rows: list[dict[str, Any]] = []
    for row in scene_adjacency.get("pairRows") or []:
        if selector_matches(row.get("selectors"), selector):
            rows.append({
                "source": row.get("source"),
                "target": row.get("target"),
                "occurrenceCount": row.get("occurrenceCount"),
                "selectors": row.get("selectors") or [],
                "leafPointers": row.get("leafPointers") or [],
                "sourceRecords": row.get("sourceRecords") or [],
                "targetRecords": row.get("targetRecords") or [],
                "strictEventBacked": bool(row.get("strictEventBacked")),
                "confirmedReviewBacked": bool(row.get("confirmedReviewBacked")),
                "selectorAdjacencyOnly": bool(row.get("selectorAdjacencyOnly")),
                "classification": row.get("classification"),
            })
    return rows


def selected_pointer_path_for(
    selected_pointer_paths: dict[str, Any],
    selector: str | None,
) -> dict[str, Any]:
    if not selector:
        return {}
    for row in selected_pointer_paths.get("selectorRootScans") or []:
        if selector_matches(row.get("selectorLabels"), selector):
            return {
                "primarySelector": row.get("primarySelector"),
                "selectorLabels": row.get("selectorLabels") or [],
                "rootHex": row.get("rootHex"),
                "rangeHex": row.get("rangeHex"),
                "section": row.get("section"),
                "isCurrentRoot": bool(row.get("isCurrentRoot")),
                "fieldMaps": row.get("fieldMaps") or [],
                "fieldMapCount": row.get("fieldMapCount"),
                "opcode07Count": row.get("opcode07Count"),
                "opcode08Count": row.get("opcode08Count"),
                "opcode09Count": row.get("opcode09Count"),
                "opcode07SelectsCurrentRootCount": row.get("opcode07SelectsCurrentRootCount"),
                "opcode07SelectsCurrentRangeCount": row.get("opcode07SelectsCurrentRangeCount"),
                "opcode09StoresCurrentRootCount": row.get("opcode09StoresCurrentRootCount"),
                "opcode09StoresCurrentRangeCount": row.get("opcode09StoresCurrentRangeCount"),
                "opcode08NearestCurrentRootProducerCount": row.get(
                    "opcode08NearestCurrentRootProducerCount"
                ),
                "opcode08NearestCurrentRangeProducerCount": row.get(
                    "opcode08NearestCurrentRangeProducerCount"
                ),
                "promotingCandidateCount": row.get("promotingCandidateCount"),
            }
    return {}


def selector_label(row: dict[str, Any]) -> str:
    if row.get("label"):
        return str(row.get("label"))
    return f"{row.get('group')}:{row.get('slot')}"


def static_selector_row(save_scene_selectors: list[dict[str, Any]], selector: str | None) -> dict[str, Any]:
    if not selector:
        return {}
    for row in save_scene_selectors or []:
        if selector_label(row) == selector:
            return {
                "selector": selector,
                "selectedPointerHex": row.get("selectedPointerHex"),
                "rowPointerHex": row.get("rowPointerHex"),
                "fieldMaps": row.get("fieldMaps") or [],
                "linkedCns": row.get("linkedCns") or [],
                "linkedScanDepth": row.get("linkedScanDepth"),
                "linkedScannedPointers": row.get("linkedScannedPointers"),
            }
    return {}


def selector_resource_refs(
    save_scene_selector_refs: list[dict[str, Any]],
    selector: str | None,
) -> list[dict[str, Any]]:
    refs: list[dict[str, Any]] = []
    if not selector:
        return refs
    for row in save_scene_selector_refs or []:
        if row.get("label") != selector:
            continue
        refs.append({
            "resource": row.get("resource"),
            "filename": row.get("filename"),
            "kind": row.get("kind"),
            "sceneMatched": bool(row.get("sceneMatched")),
            "sceneIdHex": row.get("sceneIdHex"),
            "tilesets": row.get("tilesets") or [],
            "depth": row.get("depth"),
            "pathHex": row.get("pathHex") or [],
        })
    return refs


def current_path_candidate_count(selected_path: dict[str, Any]) -> int:
    return sum(
        int(selected_path.get(key) or 0)
        for key in [
            "opcode07SelectsCurrentRootCount",
            "opcode07SelectsCurrentRangeCount",
            "opcode09StoresCurrentRootCount",
            "opcode09StoresCurrentRangeCount",
            "opcode08NearestCurrentRootProducerCount",
            "opcode08NearestCurrentRangeProducerCount",
            "promotingCandidateCount",
        ]
    )


def selector_runtime_context(
    selector: str,
    sample_count: int,
    total_sample_count: int,
    files: list[dict[str, Any]],
    mapset_aliases: dict[str, Any],
    scene_adjacency: dict[str, Any],
    selected_pointer_paths: dict[str, Any],
    save_scene_selectors: list[dict[str, Any]],
    save_scene_selector_refs: list[dict[str, Any]],
    role: str,
) -> dict[str, Any]:
    alias_group = find_alias_group(mapset_aliases, selector)
    alias = alias_row(alias_group, selector)
    selected_path = selected_pointer_path_for(selected_pointer_paths, selector)
    static_row = static_selector_row(save_scene_selectors, selector)
    refs = selector_resource_refs(save_scene_selector_refs, selector)
    field_maps = (
        selected_path.get("fieldMaps")
        or alias.get("fieldMaps")
        or alias_group.get("fieldMaps")
        or static_row.get("fieldMaps")
        or []
    )
    adjacency_rows = scene_adjacency_rows(scene_adjacency, selector)
    strict_count = sum(1 for row in adjacency_rows if row.get("strictEventBacked"))
    confirmed_count = sum(1 for row in adjacency_rows if row.get("confirmedReviewBacked"))
    selector_only_count = sum(1 for row in adjacency_rows if row.get("selectorAdjacencyOnly"))
    resource_scene_matched_count = sum(1 for row in refs if row.get("sceneMatched"))
    resource_names = sorted({str(row.get("filename")) for row in refs if row.get("filename")})
    selected_path_current_count = current_path_candidate_count(selected_path)
    contains_source = bool(alias_group.get("containsSource")) or SOURCE_MAP in field_maps
    contains_target = bool(alias_group.get("containsTarget")) or TARGET_MAP in field_maps
    route_promotion_found = bool(
        selector == TARGET_SELECTOR
        or contains_source
        or strict_count
        or confirmed_count
        or selected_path_current_count
        or selected_pointer_paths.get("selectedRootExecutionRefFound")
    )
    if selector == PUBLIC_PREDECESSOR_SELECTOR:
        classification = "public-predecessor-target-side-non-promoting"
    elif not field_maps and refs and resource_scene_matched_count == 0:
        classification = "resource-only-non-field-map-selector"
    elif (
        field_maps
        and not route_promotion_found
        and not contains_source
        and not contains_target
        and selector_only_count == len(adjacency_rows)
    ):
        classification = "non-route-field-map-selector-adjacency-only"
    else:
        classification = "runtime-selector-context-unclassified"
    return {
        "selector": selector,
        "role": role,
        "classification": classification,
        "sampleCount": sample_count,
        "sampleShare": sample_count / total_sample_count if total_sample_count else 0,
        "observedFileCount": len(selector_context_rows(files, selector)),
        "fileRows": selector_context_rows(files, selector),
        "rootHex": selected_path.get("rootHex") or static_row.get("selectedPointerHex") or alias.get("rootHex"),
        "fieldMaps": field_maps,
        "selectorEqualsTargetSelector": selector == TARGET_SELECTOR,
        "selectorInRoutePair": selector in {PUBLIC_PREDECESSOR_SELECTOR, TARGET_SELECTOR},
        "containsSourceMap": contains_source,
        "containsTargetMap": contains_target,
        "aliasGroupSelectors": [
            row.get("selector")
            for row in alias_group.get("aliases") or []
            if isinstance(row, dict) and row.get("selector")
        ],
        "aliasGroupFieldMaps": alias_group.get("fieldMaps") or field_maps,
        "aliasRootHex": alias.get("rootHex"),
        "aliasRootAddressOrderIndex": alias.get("rootAddressOrderIndex"),
        "aliasSelectedPointerEntryHex": alias.get("selectedPointerEntryHex"),
        "aliasFillCount": alias.get("fillCount"),
        "staticSelectorRow": static_row,
        "staticSelectorRowFound": bool(static_row),
        "resourceReferenceCount": len(refs),
        "resourceSceneMatchedCount": resource_scene_matched_count,
        "resourceOnly": bool(refs and not field_maps and resource_scene_matched_count == 0),
        "resourceNames": resource_names,
        "resourceRefs": refs[:12],
        "sceneAdjacencyRows": adjacency_rows,
        "sceneAdjacencyRowCount": len(adjacency_rows),
        "sceneAdjacencySelectorOnlyPairCount": selector_only_count,
        "sceneAdjacencyStrictEventBackedCount": strict_count,
        "sceneAdjacencyConfirmedReviewBackedCount": confirmed_count,
        "selectedPointerPath": selected_path,
        "selectedPointerPathFound": bool(selected_path),
        "selectedPointerPathSelectsOrStoresCurrentCount": selected_path_current_count,
        "selectedRootExecutionRefFound": bool(selected_pointer_paths.get("selectedRootExecutionRefFound")),
        "routePromotionEvidenceFound": route_promotion_found,
        "conclusion": selector_context_conclusion(selector, classification),
    }


def selector_context_conclusion(selector: str, classification: str) -> str:
    if classification == "resource-only-non-field-map-selector":
        return (
            f"Selector {selector} resolves to non-scene CNS resource references with no field maps, "
            "so its runtime samples are menu/resource diagnostics, not map1_01a -> map2_02d route proof."
        )
    if classification == "non-route-field-map-selector-adjacency-only":
        return (
            f"Selector {selector} resolves to a different field-map alias group whose adjacency rows are "
            "selector-list metadata only and have no strict/confirmed route edge or current-root producer."
        )
    if classification == "public-predecessor-target-side-non-promoting":
        return (
            "The public predecessor selector 1:0 belongs to the target-side map set, but the observed runtime "
            "attempts never transition from it into selector 2:0/current root or the expected branch-state fill."
        )
    return "This selector context is retained as non-promoting until route/current execution evidence exists."


def summarize_file(path: Path) -> dict[str, Any]:
    data = load_json(path)
    rows = data.get("rows") or []
    counts = selector_counts(rows)
    non_route_counts = {
        selector: count
        for selector, count in counts.items()
        if selector not in {"1:0", "2:0"}
    }
    return {
        "path": str(path.relative_to(ROOT)),
        "label": short_label(path),
        "objective": data.get("objective") or "",
        "promotionStatus": data.get("promotionStatus") or "unknown",
        "targetSelector": data.get("targetSelector") or "2:0",
        "sampleCount": int(data.get("sampleCount") or 0),
        "sequenceCount": int(data.get("sequenceCount") or len(rows)),
        "observedSelectors": data.get("observedSelectors") or sorted(counts),
        "observedPublicSaveSelectors": data.get("observedPublicSaveSelectors") or [],
        "anyReachedPublicSaveSelector": data.get("anyReachedPublicSaveSelector") is True,
        "anyReachedRouteSelectorContext": data.get("anyReachedRouteSelectorContext") is True,
        "anyReachedCurrentRoot": data.get("anyReachedCurrentRoot") is True,
        "selectorCounts": dict(sorted(counts.items())),
        "nonRouteSelectorCounts": dict(sorted(non_route_counts.items())),
        "routeSelectorHitCount": sum(int(row.get("routeSelectorHitCount") or 0) for row in rows),
        "currentRootHitCount": sum(int(row.get("currentRootHitCount") or 0) for row in rows),
        "activeOrderValueCounts": watch_value_counts(rows, "activeOrder"),
        "activeSlotDescriptorValueCounts": watch_value_counts(rows, "activeSlot"),
        "branchStateValueCounts": watch_value_counts(rows, "secondaryBranchState"),
        "conclusion": data.get("conclusion") or "",
    }


def build_summary(
    out_dir: Path = OUT,
    mapset_aliases: dict[str, Any] | None = None,
    scene_adjacency: dict[str, Any] | None = None,
    selected_pointer_paths: dict[str, Any] | None = None,
    save_scene_selectors: list[dict[str, Any]] | None = None,
    save_scene_selector_refs: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
    mapset_aliases = mapset_aliases or load_optional_json(MAPSET_ALIASES)
    scene_adjacency = scene_adjacency or load_optional_json(SCENE_ADJACENCY_INDEX)
    selected_pointer_paths = selected_pointer_paths or load_optional_json(GLOBAL_SELECTED_POINTER_PATHS)
    save_scene_selectors = save_scene_selectors or load_optional_json(SAVE_SCENE_SELECTORS) or []
    save_scene_selector_refs = save_scene_selector_refs or load_optional_json(SAVE_SCENE_SELECTOR_REFS) or []
    paths = sorted(out_dir.glob("runtime_selected_pointer_predecessor_*poll.json"))
    files = [summarize_file(path) for path in paths]
    selector_totals: Counter[str] = Counter()
    non_route_totals: Counter[str] = Counter()
    active_order_totals: Counter[str] = Counter()
    branch_state_totals: Counter[str] = Counter()
    for item in files:
        selector_totals.update(item["selectorCounts"])
        non_route_totals.update(item["nonRouteSelectorCounts"])
        active_order_totals.update(item["activeOrderValueCounts"])
        branch_state_totals.update(item["branchStateValueCounts"])
    route_selector_hit_count = sum(item["routeSelectorHitCount"] for item in files)
    current_root_hit_count = sum(item["currentRootHitCount"] for item in files)
    public_selector_observed_count = sum(1 for item in files if item["anyReachedPublicSaveSelector"])
    total_sample_count = sum(item["sampleCount"] for item in files)
    diversion_contexts = [
        selector_runtime_context(
            selector,
            count,
            total_sample_count,
            files,
            mapset_aliases,
            scene_adjacency,
            selected_pointer_paths,
            save_scene_selectors,
            save_scene_selector_refs,
            "observed-non-route-selector",
        )
        for selector, count in sorted(
            non_route_totals.items(),
            key=lambda item: (-item[1], item[0]),
        )
    ]
    public_predecessor_context = selector_runtime_context(
        PUBLIC_PREDECESSOR_SELECTOR,
        selector_totals.get(PUBLIC_PREDECESSOR_SELECTOR, 0),
        total_sample_count,
        files,
        mapset_aliases,
        scene_adjacency,
        selected_pointer_paths,
        save_scene_selectors,
        save_scene_selector_refs,
        "public-predecessor-selector",
    )
    return {
        "source": "runtime predecessor route attempt context",
        "sourcePattern": "out/runtime_selected_pointer_predecessor_*poll.json",
        "sourceFileCount": len(files),
        "sourceFiles": [item["path"] for item in files],
        "publicPredecessorSelector": PUBLIC_PREDECESSOR_SELECTOR,
        "targetSelector": TARGET_SELECTOR,
        "currentRootHex": CURRENT_ROOT_HEX,
        "promotionStatus": "blocked",
        "proofFound": False,
        "predecessorRouteAttemptProofFound": False,
        "publicPredecessorObservedFileCount": public_selector_observed_count,
        "anyReachedRouteSelectorContext": any(item["anyReachedRouteSelectorContext"] for item in files),
        "anyReachedCurrentRoot": any(item["anyReachedCurrentRoot"] for item in files),
        "routeSelectorHitCount": route_selector_hit_count,
        "currentRootHitCount": current_root_hit_count,
        "totalSampleCount": total_sample_count,
        "totalSequenceCount": sum(item["sequenceCount"] for item in files),
        "observedSelectorCounts": dict(sorted(selector_totals.items())),
        "nonRouteSelectorCounts": dict(sorted(non_route_totals.items())),
        "activeOrderValueCounts": dict(sorted(active_order_totals.items())),
        "branchStateValueCounts": dict(sorted(branch_state_totals.items())),
        "dominantDiversionSelector": diversion_contexts[0]["selector"] if diversion_contexts else None,
        "diversionSelectorContextCount": len(diversion_contexts),
        "fieldMapDiversionSelectorCount": sum(
            1 for row in diversion_contexts if row.get("fieldMaps")
        ),
        "resourceOnlyDiversionSelectorCount": sum(
            1 for row in diversion_contexts if row.get("resourceOnly")
        ),
        "diversionRoutePromotionEvidenceFound": any(
            row.get("routePromotionEvidenceFound") for row in diversion_contexts
        ),
        "diversionSelectorContexts": diversion_contexts,
        "publicPredecessorSelectorContext": public_predecessor_context,
        "failedPredecessorRouteAttemptGateIds": [
            "route-selector-2-0-observation",
            "current-root-observation",
            "predecessor-to-current-runtime-transition",
            "strict-source-hotspot",
        ],
        "missingEvidence": [
            "runtime observation that public predecessor selector 1:0 reaches selector 2:0",
            "runtime observation that selected pointer becomes current root 0x00540714",
            "control-flow proof that predecessor route attempts select the current 2:0 root",
            "strict map1_01a source hotspot or equivalent runtime trigger",
        ],
        "evidenceRefs": EVIDENCE_REFS,
        "evidenceRefCount": len(EVIDENCE_REFS),
        "files": files,
        "conclusion": (
            "Existing predecessor-save route-attempt polls observe the public predecessor selector in some "
            "runs, but none reach selector 2:0 or current root 0x00540714. Observed diversions such as "
            "19:1 and 50:0 remain non-route diagnostics, so predecessor runtime attempts do not replace "
            "selected-root execution or strict-source-hotspot proof."
        ),
    }


def markdown(summary: dict[str, Any]) -> str:
    diversion_contexts = summary.get("diversionSelectorContexts") or []
    public_context = summary.get("publicPredecessorSelectorContext") or {}
    lines = [
        "# Runtime Predecessor Route Attempt Context",
        "",
        summary["conclusion"],
        "",
        "## Summary",
        "",
        f"- source files: {summary['sourceFileCount']}",
        f"- total sequences/samples: {summary['totalSequenceCount']}/{summary['totalSampleCount']}",
        f"- public predecessor selector observed in files: {summary['publicPredecessorObservedFileCount']}",
        f"- route selector hit count: {summary['routeSelectorHitCount']}",
        f"- current root hit count: {summary['currentRootHitCount']}",
        f"- dominant diversion selector: `{summary.get('dominantDiversionSelector')}`",
        f"- diversion contexts: {summary.get('diversionSelectorContextCount')} "
        f"(field-map {summary.get('fieldMapDiversionSelectorCount')}, "
        f"resource-only {summary.get('resourceOnlyDiversionSelectorCount')})",
        f"- diversion route promotion evidence found: {summary.get('diversionRoutePromotionEvidenceFound')}",
        f"- proof found: `{summary['proofFound']}`",
        f"- predecessor route attempt proof found: `{summary['predecessorRouteAttemptProofFound']}`",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        "## Selector Counts",
        "",
        "| selector | samples |",
        "| --- | ---: |",
    ]
    for selector, count in summary["observedSelectorCounts"].items():
        lines.append(f"| {selector} | {count} |")
    lines.extend(
        [
            "",
            "## Diversion Selector Contexts",
            "",
            "| selector | classification | samples | root | maps/resources | adjacency/strict/confirmed | current proof | route proof |",
            "| --- | --- | ---: | --- | --- | --- | ---: | --- |",
        ]
    )
    for item in diversion_contexts:
        maps = ", ".join(item.get("fieldMaps") or [])
        resources = ", ".join((item.get("resourceNames") or [])[:6])
        maps_or_resources = maps or resources or "-"
        lines.append(
            f"| `{item.get('selector')}` | `{item.get('classification')}` | {item.get('sampleCount')} | "
            f"`{item.get('rootHex')}` | `{maps_or_resources}` | "
            f"{item.get('sceneAdjacencyRowCount')}/"
            f"{item.get('sceneAdjacencyStrictEventBackedCount')}/"
            f"{item.get('sceneAdjacencyConfirmedReviewBackedCount')} | "
            f"{item.get('selectedPointerPathSelectsOrStoresCurrentCount')} | "
            f"{item.get('routePromotionEvidenceFound')} |"
        )
    lines.extend(
        [
            "",
            "## Public Predecessor Selector",
            "",
            f"- selector: `{public_context.get('selector')}`",
            f"- classification: `{public_context.get('classification')}`",
            f"- samples: {public_context.get('sampleCount')}",
            f"- field maps: `{', '.join(public_context.get('fieldMaps') or []) or '-'}`",
            f"- current proof count: {public_context.get('selectedPointerPathSelectsOrStoresCurrentCount')}",
            f"- route promotion evidence found: {public_context.get('routePromotionEvidenceFound')}",
            "",
            public_context.get("conclusion") or "",
        ]
    )
    lines.extend(
        [
            "",
            "## Files",
            "",
            "| file | selectors | public 1:0 | route 2:0 | current root | samples | sequences |",
            "| --- | --- | ---: | ---: | ---: | ---: | ---: |",
        ]
    )
    for item in summary["files"]:
        selectors = ", ".join(f"{key}:{value}" for key, value in item["selectorCounts"].items()) or "-"
        lines.append(
            f"| `{item['path']}` | {selectors} | "
            f"{str(item['anyReachedPublicSaveSelector']).lower()} | "
            f"{item['routeSelectorHitCount']} | {item['currentRootHitCount']} | "
            f"{item['sampleCount']} | {item['sequenceCount']} |"
        )
    lines.extend(
        [
            "",
            "## Missing Evidence",
            "",
        ]
    )
    for item in summary["missingEvidence"]:
        lines.append(f"- {item}")
    lines.extend(["", "## Evidence Refs", ""])
    for ref in summary["evidenceRefs"]:
        lines.append(f"- `{ref['path']}`: {', '.join(ref.get('fields') or [])}")
    return "\n".join(lines) + "\n"


def html_page(summary: dict[str, Any]) -> str:
    selector_rows = "\n".join(
        f"<tr><td>{html.escape(selector)}</td><td>{count}</td></tr>"
        for selector, count in summary["observedSelectorCounts"].items()
    )
    file_rows = "\n".join(
        "<tr>"
        f"<td>{html.escape(item['path'])}</td>"
        f"<td>{html.escape(', '.join(f'{key}:{value}' for key, value in item['selectorCounts'].items()) or '-')}</td>"
        f"<td>{str(item['anyReachedPublicSaveSelector']).lower()}</td>"
        f"<td>{item['routeSelectorHitCount']}</td>"
        f"<td>{item['currentRootHitCount']}</td>"
        f"<td>{item['sampleCount']}</td>"
        f"<td>{item['sequenceCount']}</td>"
        "</tr>"
        for item in summary["files"]
    )
    diversion_rows = "\n".join(
        "<tr>"
        f"<td>{html.escape(str(item.get('selector')))}</td>"
        f"<td>{html.escape(str(item.get('classification')))}</td>"
        f"<td>{item.get('sampleCount')}</td>"
        f"<td>{html.escape(str(item.get('rootHex')))}</td>"
        f"<td>{html.escape(', '.join(item.get('fieldMaps') or (item.get('resourceNames') or [])[:6]) or '-')}</td>"
        f"<td>{item.get('sceneAdjacencyRowCount')}/"
        f"{item.get('sceneAdjacencyStrictEventBackedCount')}/"
        f"{item.get('sceneAdjacencyConfirmedReviewBackedCount')}</td>"
        f"<td>{item.get('selectedPointerPathSelectsOrStoresCurrentCount')}</td>"
        f"<td>{str(item.get('routePromotionEvidenceFound')).lower()}</td>"
        "</tr>"
        for item in summary.get("diversionSelectorContexts") or []
    )
    missing = "".join(f"<li>{html.escape(item)}</li>" for item in summary["missingEvidence"])
    refs = "".join(
        f"<li><code>{html.escape(ref['path'])}</code>: {html.escape(', '.join(ref.get('fields') or []))}</li>"
        for ref in summary["evidenceRefs"]
    )
    public_context = summary.get("publicPredecessorSelectorContext") or {}
    return f"""<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <title>Runtime Predecessor Route Attempt Context</title>
  <style>
    body {{ font-family: system-ui, sans-serif; margin: 24px; line-height: 1.5; color: #202124; }}
    table {{ border-collapse: collapse; width: 100%; margin: 16px 0 28px; }}
    th, td {{ border: 1px solid #d7dce2; padding: 6px 8px; text-align: left; vertical-align: top; }}
    th {{ background: #f4f6f8; }}
    code {{ background: #f4f6f8; padding: 1px 4px; }}
  </style>
</head>
<body>
  <h1>Runtime Predecessor Route Attempt Context</h1>
  <p>{html.escape(summary['conclusion'])}</p>
  <h2>Summary</h2>
  <ul>
    <li>source files: {summary['sourceFileCount']}</li>
    <li>total sequences/samples: {summary['totalSequenceCount']}/{summary['totalSampleCount']}</li>
    <li>public predecessor selector observed in files: {summary['publicPredecessorObservedFileCount']}</li>
    <li>route selector hit count: {summary['routeSelectorHitCount']}</li>
    <li>current root hit count: {summary['currentRootHitCount']}</li>
    <li>dominant diversion selector: <code>{html.escape(str(summary.get('dominantDiversionSelector')))}</code></li>
    <li>diversion contexts: {summary.get('diversionSelectorContextCount')} (field-map {summary.get('fieldMapDiversionSelectorCount')}, resource-only {summary.get('resourceOnlyDiversionSelectorCount')})</li>
    <li>diversion route promotion evidence found: {str(summary.get('diversionRoutePromotionEvidenceFound')).lower()}</li>
    <li>proof found: <code>{summary['proofFound']}</code></li>
    <li>predecessor route attempt proof found: <code>{summary['predecessorRouteAttemptProofFound']}</code></li>
    <li>promotion status: <code>{html.escape(summary['promotionStatus'])}</code></li>
  </ul>
  <h2>Selector Counts</h2>
  <table><thead><tr><th>selector</th><th>samples</th></tr></thead><tbody>{selector_rows}</tbody></table>
  <h2>Diversion Selector Contexts</h2>
  <table><thead><tr><th>selector</th><th>classification</th><th>samples</th><th>root</th><th>maps/resources</th><th>adjacency/strict/confirmed</th><th>current proof</th><th>route proof</th></tr></thead><tbody>{diversion_rows}</tbody></table>
  <h2>Public Predecessor Selector</h2>
  <ul>
    <li>selector: <code>{html.escape(str(public_context.get('selector')))}</code></li>
    <li>classification: <code>{html.escape(str(public_context.get('classification')))}</code></li>
    <li>samples: {public_context.get('sampleCount')}</li>
    <li>field maps: <code>{html.escape(', '.join(public_context.get('fieldMaps') or []) or '-')}</code></li>
    <li>current proof count: {public_context.get('selectedPointerPathSelectsOrStoresCurrentCount')}</li>
    <li>route promotion evidence found: {str(public_context.get('routePromotionEvidenceFound')).lower()}</li>
  </ul>
  <p>{html.escape(public_context.get('conclusion') or '')}</p>
  <h2>Files</h2>
  <table><thead><tr><th>file</th><th>selectors</th><th>public 1:0</th><th>route 2:0</th><th>current root</th><th>samples</th><th>sequences</th></tr></thead><tbody>{file_rows}</tbody></table>
  <h2>Missing Evidence</h2>
  <ul>{missing}</ul>
  <h2>Evidence Refs</h2>
  <ul>{refs}</ul>
</body>
</html>
"""


def write_outputs(summary: dict[str, Any], out_dir: Path = OUT, html_out: Path | None = None) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "runtime_predecessor_route_attempt_context.json").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")


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--out-dir", type=Path, default=OUT)
    parser.add_argument(
        "--html-out",
        type=Path,
        default=None,
        help="Optional HTML review path. By default only the JSON evidence is written.",
    )
    args = parser.parse_args()
    summary = build_summary(args.out_dir)
    write_outputs(summary, args.out_dir, args.html_out)
    print(f"wrote runtime predecessor route attempt context -> {args.out_dir / 'runtime_predecessor_route_attempt_context.json'}")


if __name__ == "__main__":
    main()
