#!/usr/bin/env python3
"""Contrast the confirmed map1_02b entry pattern with the map1_01a frontier."""
from __future__ import annotations

import argparse
import html
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
SOURCE = "map1_01a"
TARGET = "map2_02d"
CONFIRMED_SOURCE = "map1_02b"
CONFIRMED_TARGET = "map1_01a"


def hex32(value: int) -> str:
    return f"0x{value:08x}"


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


def cluster_range(cluster: dict | None) -> str:
    if not cluster:
        return "-"
    return f"{cluster.get('clusterStartHex')}..{cluster.get('clusterEndHex')}"


def event_record_hex(event: dict) -> str:
    if isinstance(event.get("recordVa"), int):
        return hex32(event["recordVa"])
    return event.get("recordVaHex") or "-"


def find_cluster(
    field_map_roots: dict,
    *,
    classification: str | None = None,
    manifest_map: str | None = None,
    event_source: str | None = None,
    event_link: str | None = None,
    current_frontier: bool | None = None,
) -> dict | None:
    for cluster in field_map_roots.get("clusters") or []:
        if classification and cluster.get("classification") != classification:
            continue
        if manifest_map and manifest_map not in (cluster.get("manifestMaps") or []):
            continue
        if event_source and event_source not in (cluster.get("eventSources") or []):
            continue
        if event_link and event_link not in (cluster.get("eventFieldLinks") or []):
            continue
        if current_frontier is not None and bool(cluster.get("currentFrontierPairCount")) is not current_frontier:
            continue
        return cluster
    return None


def find_event(events: list[dict], source: str, target: str) -> dict | None:
    for event in events:
        if event.get("map") != source:
            continue
        links = []
        for ref in event.get("eventDispatchRefs") or []:
            for value in ref.get("conditionLinkedStrings") or []:
                if value.endswith(".cns"):
                    links.append(value[:-4])
        if target in links:
            return event
    return None


def review_counts(transition_reviews: dict, source: str, target: str) -> dict:
    rows = [
        row
        for row in transition_reviews.values()
        if row.get("source") == source and row.get("target") == target
    ]
    return {
        "total": len(rows),
        "confirmed": sum(1 for row in rows if row.get("state") == "confirmed"),
        "rejected": sum(1 for row in rows if row.get("state") == "rejected"),
        "confirmedTiles": [
            {"x": row.get("x"), "y": row.get("y")}
            for row in rows
            if row.get("state") == "confirmed"
        ],
    }


def scene_list_frontier_row(scene_list_context: dict) -> dict:
    rows = scene_list_context.get("rows") or []
    return next(
        (
            row for row in rows
            if row.get("source") == SOURCE and row.get("target") == TARGET
        ),
        {},
    )


def build_summary(
    field_map_roots: dict,
    events: list[dict],
    transition_reviews: dict,
    resource_ref_scan: dict,
    scene_list_context: dict,
    scene_record_sequence: dict,
    coordinate_variant_scan: dict,
) -> dict:
    confirmed_cluster = find_cluster(
        field_map_roots,
        classification="strict event-linked cluster",
        manifest_map=CONFIRMED_TARGET,
        event_source=CONFIRMED_SOURCE,
        event_link=CONFIRMED_TARGET,
    )
    frontier_cluster = find_cluster(
        field_map_roots,
        classification="current frontier selector-only cluster",
        manifest_map=SOURCE,
        current_frontier=True,
    )
    confirmed_event = find_event(events, CONFIRMED_SOURCE, CONFIRMED_TARGET)
    scene_row = scene_list_frontier_row(scene_list_context)
    branch_steps = scene_row.get("branchSteps") or []
    branch_step = branch_steps[0] if branch_steps else {}
    reviews = review_counts(transition_reviews, CONFIRMED_SOURCE, CONFIRMED_TARGET)
    confirmed_pattern = {
        "route": f"{CONFIRMED_SOURCE} -> {CONFIRMED_TARGET}",
        "clusterRangeHex": cluster_range(confirmed_cluster),
        "classification": (confirmed_cluster or {}).get("classification"),
        "eventRecordCount": (confirmed_cluster or {}).get("eventRecordCount", 0),
        "eventRecordHex": event_record_hex(confirmed_event or {}),
        "eventPointCount": (confirmed_event or {}).get("rawPointCount") or len((confirmed_event or {}).get("points") or []),
        "eventActivePointCount": len((confirmed_event or {}).get("activePoints") or []),
        "eventDispatchRefCount": len((confirmed_event or {}).get("eventDispatchRefs") or []),
        "eventFieldLinks": (confirmed_cluster or {}).get("eventFieldLinks") or [],
        "transitionReviewTotal": reviews["total"],
        "transitionReviewConfirmed": reviews["confirmed"],
        "transitionReviewRejected": reviews["rejected"],
        "confirmedTiles": reviews["confirmedTiles"],
    }
    frontier_pattern = {
        "route": f"{SOURCE} -> {TARGET}",
        "clusterRangeHex": cluster_range(frontier_cluster),
        "classification": (frontier_cluster or {}).get("classification"),
        "eventRecordCount": (frontier_cluster or {}).get("eventRecordCount", 0),
        "saveSelectorRefCount": (frontier_cluster or {}).get("saveSelectorRefCount", 0),
        "currentFrontierPairCount": (frontier_cluster or {}).get("currentFrontierPairCount", 0),
        "resourceReferenceCount": resource_ref_scan.get("resourceReferenceCount", 0),
        "routeExitPointCandidateCount": resource_ref_scan.get("routeExitPointCandidateCount", 0),
        "strictSourceTargetCandidateCount": resource_ref_scan.get("strictSourceTargetCandidateCount", 0),
        "selectorOnlySceneList": scene_row.get("selectorOnlySceneList"),
        "branchTargetKind": branch_step.get("branchTargetKind"),
        "branchClassification": branch_step.get("classification"),
        "nearestSourceRecordAfterBranch": (branch_step.get("nearestSourceRecordAfterBranch") or {}).get("recordVaHex"),
        "nearestTargetRecordAfterBranch": (branch_step.get("nearestTargetRecordAfterBranch") or {}).get("recordVaHex"),
        "adjacentSceneRecordCount": scene_record_sequence.get("adjacentSceneRecordCount", 0),
        "geometryExitWordHitCount": scene_record_sequence.get("geometryExitWordHitCount", 0),
        "strictCoordinateEvidenceFound": coordinate_variant_scan.get("strictCoordinateEvidenceFound"),
    }
    missing = [
        "strict event record for map1_01a as source",
        "event dispatch condition linking map1_01a to map2_02d",
        "event point table or active tile points for the source map",
        "route-exit point candidate in nearby map1_01a/map2_02d resource windows",
        "geometry exit word hit inside the current selector scene-record sequence",
    ]
    summary = {
        "source": SOURCE,
        "target": TARGET,
        "confirmedReferenceRoute": f"{CONFIRMED_SOURCE} -> {CONFIRMED_TARGET}",
        "confirmedPattern": confirmed_pattern,
        "frontierPattern": frontier_pattern,
        "confirmedLikePatternFound": False,
        "frontierHasStrictEventRecord": frontier_pattern["eventRecordCount"] > 0,
        "frontierHasStrictSourceHotspot": False,
        "frontierHasOnlySelectorSceneList": frontier_pattern["selectorOnlySceneList"] is True,
        "missingComparedToConfirmed": missing,
        "promotionStatus": "blocked",
        "conclusion": (
            "The confirmed map1_02b -> map1_01a transition has a strict event-linked cluster, an event dispatch "
            "condition, and a 24-point event table with reviewed confirmed tiles. The current map1_01a -> map2_02d "
            "frontier has adjacent scene records and many save-selector references, but no event record, no source "
            "point table, no route-exit point candidate, and no geometry exit word hit. Its branch target is a "
            "resource gate before scene records, so the pattern does not match a promotable confirmed transition."
        ),
    }
    return summary


def markdown(summary: dict) -> str:
    confirmed = summary["confirmedPattern"]
    frontier = summary["frontierPattern"]
    lines = [
        "# map1_01a Record Pattern Contrast",
        "",
        f"- route under test: `{summary['source']} -> {summary['target']}`",
        f"- confirmed reference route: `{summary['confirmedReferenceRoute']}`",
        f"- confirmed-like pattern found: {summary['confirmedLikePatternFound']}",
        f"- frontier has strict event record: {summary['frontierHasStrictEventRecord']}",
        f"- frontier has strict source hotspot: {summary['frontierHasStrictSourceHotspot']}",
        f"- frontier has only selector scene list: {summary['frontierHasOnlySelectorSceneList']}",
        f"- promotion status: `{summary['promotionStatus']}`",
        "",
        summary["conclusion"],
        "",
        "## Pattern Comparison",
        "",
        "| signal | confirmed entry | current frontier |",
        "| --- | --- | --- |",
        f"| route | `{confirmed['route']}` | `{frontier['route']}` |",
        f"| cluster | `{confirmed['clusterRangeHex']}` {confirmed['classification']} | `{frontier['clusterRangeHex']}` {frontier['classification']} |",
        f"| event records | {confirmed['eventRecordCount']} | {frontier['eventRecordCount']} |",
        f"| event record | `{confirmed['eventRecordHex']}` | - |",
        f"| event points | {confirmed['eventPointCount']} raw / {confirmed['eventActivePointCount']} active | - |",
        f"| dispatch refs | {confirmed['eventDispatchRefCount']} | - |",
        f"| event field links | {', '.join(confirmed['eventFieldLinks']) or '-'} | - |",
        f"| transition reviews | {confirmed['transitionReviewConfirmed']} confirmed / {confirmed['transitionReviewRejected']} rejected | - |",
        f"| save-selector refs | - | {frontier['saveSelectorRefCount']} |",
        f"| branch target | - | {frontier['branchTargetKind'] or '-'} ({frontier['branchClassification'] or '-'}) |",
        f"| scene adjacency | - | {frontier['adjacentSceneRecordCount']} adjacent rows |",
        f"| resource refs | - | {frontier['resourceReferenceCount']} direct refs |",
        f"| route-exit point candidates | - | {frontier['routeExitPointCandidateCount']} |",
        f"| geometry exit word hits | - | {frontier['geometryExitWordHitCount']} |",
        f"| strict coordinate evidence | - | {frontier['strictCoordinateEvidenceFound']} |",
        "",
        "## Missing Compared To Confirmed",
        "",
    ]
    lines.extend(f"- {item}" for item in summary["missingComparedToConfirmed"])
    lines.append("")
    return "\n".join(lines)


def html_page(summary: dict) -> str:
    confirmed = summary["confirmedPattern"]
    frontier = summary["frontierPattern"]
    rows = [
        ("route", confirmed["route"], frontier["route"]),
        ("cluster", f"{confirmed['clusterRangeHex']} {confirmed['classification']}", f"{frontier['clusterRangeHex']} {frontier['classification']}"),
        ("event records", str(confirmed["eventRecordCount"]), str(frontier["eventRecordCount"])),
        ("event record", confirmed["eventRecordHex"], "-"),
        ("event points", f"{confirmed['eventPointCount']} raw / {confirmed['eventActivePointCount']} active", "-"),
        ("dispatch refs", str(confirmed["eventDispatchRefCount"]), "-"),
        ("event field links", ", ".join(confirmed["eventFieldLinks"]) or "-", "-"),
        ("transition reviews", f"{confirmed['transitionReviewConfirmed']} confirmed / {confirmed['transitionReviewRejected']} rejected", "-"),
        ("save-selector refs", "-", str(frontier["saveSelectorRefCount"])),
        ("branch target", "-", f"{frontier.get('branchTargetKind') or '-'} ({frontier.get('branchClassification') or '-'})"),
        ("scene adjacency", "-", f"{frontier['adjacentSceneRecordCount']} adjacent rows"),
        ("resource refs", "-", f"{frontier['resourceReferenceCount']} direct refs"),
        ("route-exit point candidates", "-", str(frontier["routeExitPointCandidateCount"])),
        ("geometry exit word hits", "-", str(frontier["geometryExitWordHitCount"])),
        ("strict coordinate evidence", "-", str(frontier["strictCoordinateEvidenceFound"])),
    ]
    table_rows = "\n".join(
        "<tr>"
        f"<td>{html.escape(signal)}</td>"
        f"<td>{html.escape(left)}</td>"
        f"<td>{html.escape(right)}</td>"
        "</tr>"
        for signal, left, right in rows
    )
    missing = "".join(f"<li>{html.escape(item)}</li>" for item in summary["missingComparedToConfirmed"])
    return "\n".join([
        "<!doctype html>",
        '<html lang="en">',
        "<head>",
        '  <meta charset="utf-8">',
        '  <meta name="viewport" content="width=device-width, initial-scale=1">',
        "  <title>map1_01a Record Pattern Contrast</title>",
        "  <style>body{font-family:system-ui,sans-serif;background:#111;color:#eee;margin:24px}table{border-collapse:collapse;width:100%;max-width:1180px}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>map1_01a Record Pattern Contrast</h1>",
        f"  <p>route <code>{summary['source']} -> {summary['target']}</code>; confirmed reference <code>{summary['confirmedReferenceRoute']}</code>; confirmed-like pattern found: {summary['confirmedLikePatternFound']}; promotion status <code>{summary['promotionStatus']}</code>.</p>",
        f"  <p>{html.escape(summary['conclusion'])}</p>",
        "  <table><thead><tr><th>signal</th><th>confirmed entry</th><th>current frontier</th></tr></thead><tbody>",
        table_rows,
        "  </tbody></table>",
        "  <h2>Missing Compared To Confirmed</h2>",
        f"  <ul>{missing}</ul>",
        "</body>",
        "</html>",
        "",
    ])


def write_outputs(summary: dict, out_dir: Path = OUT) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "map1_01a_record_pattern_contrast.json").write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        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 / "field_map_record_roots.json", {}),
        load_json(args.out_dir / "scene_events.json", []),
        load_json(args.out_dir / "transition_reviews.json", {}),
        load_json(args.out_dir / "map1_01a_resource_ref_scan.json", {}),
        load_json(args.out_dir / "save_selector_scene_list_context.json", {}),
        load_json(args.out_dir / "save_selector_scene_record_sequence.json", {}),
        load_json(args.out_dir / "map1_01a_exit_coordinate_variant_scan.json", {}),
    )
    write_outputs(summary, args.out_dir)
    print(f"wrote map1_01a record pattern contrast -> {args.out_dir / 'map1_01a_record_pattern_contrast.json'}")


if __name__ == "__main__":
    main()
