#!/usr/bin/env python3
"""Poll source-save load variants with coordinate and branch-state watches enabled."""
from __future__ import annotations

from argparse import Namespace
import html
from typing import Any

import probe_runtime_input_path as runtime_input
import probe_runtime_predecessor_coordinate_branch_state_poll as coord_poll
from probe_runtime_input_path import DEFAULT_PREFIX, OUT
from probe_runtime_selected_pointer_multislot_savedata_poll import build_summary
from probe_runtime_selected_pointer_poll import write_outputs


OUTPUT_PREFIX = "runtime_selected_pointer_source_savedata_load_coordinate_poll"
SOURCE_SELECTOR = "0:0"
SOURCE_START_TILE = {"x": 0, "y": 2}
SLOT_SOURCES = [
    "1=data/public_savedata/HandyHwanseEditor/bin/Debug/savedat2.dat",
    "2=data/public_savedata/HandyHwanseEditor/bin/Release/savedat2.dat",
]
SEQUENCES = [
    "source-debug-slot1-down-enter=Down,Return",
    "source-debug-slot1-down-enter-enter=Down,Return,Return",
    "source-release-slot2-down-enter=Down,Return,Down,Return",
    "source-release-slot2-down-enter-enter=Down,Return,Down,Return,Return",
]


def decoded_unique_pairs(row: dict[str, Any], key: str) -> list[dict[str, Any]]:
    pairs = []
    for value in coord_poll.unique_values(row, key):
        decoded = coord_poll.unpack_pair_hex(value.get("valueHex"))
        if decoded:
            pairs.append(decoded | {"count": value.get("count"), "valueHex": value.get("valueHex")})
    return pairs


def source_tile_seen(row: dict[str, Any], key: str) -> bool:
    expected = coord_poll.pair_hex(SOURCE_START_TILE["x"], SOURCE_START_TILE["y"])
    return expected in {value.get("valueHex") for value in coord_poll.unique_values(row, key)}


def branch_state_all_zero(row: dict[str, Any]) -> bool:
    for index in range(12):
        values = coord_poll.unique_values(row, f"secondaryBranchState{index}")
        if len(values) != 1 or values[0].get("valueHex") != "0x00":
            return False
    return True


def analyze(summary: dict[str, Any]) -> dict[str, Any]:
    rows = []
    any_source = SOURCE_SELECTOR in (summary.get("observedPublicSaveSelectors") or [])
    any_source_tile = False
    any_route = bool(summary.get("anyReachedRouteSelectorContext"))
    for row in summary.get("rows") or []:
        selectors = [
            item.get("selector") if isinstance(item, dict) else str(item)
            for item in row.get("uniqueSelectorContexts") or []
        ]
        camera_source = source_tile_seen(row, "cameraTilePair")
        actor_source_slots = [
            slot
            for slot in range(coord_poll.ACTOR_POINTER_SLOT_COUNT)
            if source_tile_seen(row, f"actor{slot}TilePair")
        ]
        trail_source_slots = [
            slot
            for slot in range(coord_poll.ACTOR_HISTORY_SLOT_COUNT)
            if source_tile_seen(row, f"trail{slot}TilePair")
        ]
        row_source_tile = bool(camera_source or actor_source_slots or trail_source_slots)
        any_source_tile = any_source_tile or row_source_tile
        rows.append({
            "name": row.get("name"),
            "sampleCount": row.get("sampleCount"),
            "selectors": selectors,
            "sourceSelectorObserved": SOURCE_SELECTOR in selectors,
            "cameraPairs": decoded_unique_pairs(row, "cameraTilePair"),
            "cameraSourceStartObserved": camera_source,
            "actorSourceStartSlots": actor_source_slots,
            "trailSourceStartSlots": trail_source_slots,
            "routeSelectorHitCount": row.get("routeSelectorHitCount"),
            "currentRootHitCount": row.get("currentRootHitCount"),
            "branchStateAllZero": branch_state_all_zero(row),
        })
    if any_route:
        classification = "route-selector-observed"
    elif any_source and any_source_tile:
        classification = "source-selector-and-start-coordinate-observed"
    elif any_source:
        classification = "source-selector-observed-without-start-coordinate"
    else:
        classification = "source-selector-not-observed"
    return {
        "classification": classification,
        "sourceSelectorObserved": any_source,
        "sourceStartTileObserved": any_source_tile,
        "rows": rows,
    }


def coordinate_markdown(summary: dict[str, Any]) -> str:
    analysis = summary.get("sourceLoadCoordinateAnalysis") or {}
    lines = [
        "",
        "## Source Load Coordinate Poll",
        "",
        f"- classification: `{analysis.get('classification')}`",
        f"- source selector observed: {analysis.get('sourceSelectorObserved')}",
        f"- source start tile `{SOURCE_START_TILE['x']},{SOURCE_START_TILE['y']}` observed: {analysis.get('sourceStartTileObserved')}",
        "",
        "| sequence | samples | selectors | camera pairs | camera start | actor start | trail start | route/current | branch state |",
        "| --- | ---: | --- | --- | --- | --- | --- | --- | --- |",
    ]
    for row in analysis.get("rows") or []:
        pairs = "; ".join(
            f"{pair.get('x')},{pair.get('y')}x{pair.get('count')}"
            for pair in row.get("cameraPairs") or []
        )
        lines.append(
            f"| `{row.get('name')}` | {row.get('sampleCount')} | `{','.join(row.get('selectors') or []) or '-'}` | "
            f"`{pairs or '-'}` | {row.get('cameraSourceStartObserved')} | "
            f"`{','.join(str(item) for item in row.get('actorSourceStartSlots') or []) or '-'}` | "
            f"`{','.join(str(item) for item in row.get('trailSourceStartSlots') or []) or '-'}` | "
            f"{row.get('routeSelectorHitCount')}/{row.get('currentRootHitCount')} | "
            f"{'all-zero' if row.get('branchStateAllZero') else 'mixed'} |"
        )
    return "\n".join(lines)


def coordinate_html(summary: dict[str, Any]) -> str:
    return "\n".join([
        "<h2>Source Load Coordinate Poll</h2>",
        f"<pre>{html.escape(coordinate_markdown(summary))}</pre>",
    ])


def write_coordinate_outputs(summary: dict[str, Any]) -> None:
    write_outputs(summary, OUT, OUTPUT_PREFIX)
    html_path = OUT / f"{OUTPUT_PREFIX}.html"
    html_path.write_text(html_path.read_text(encoding="utf-8") + coordinate_html(summary), encoding="utf-8")


def main() -> None:
    runtime_input.ROUTE_WATCH_VALUES = dict(runtime_input.ROUTE_WATCH_VALUES)
    runtime_input.ROUTE_WATCH_VALUES.update(coord_poll.BRANCH_STATE_WATCH_VALUES)
    coord_poll.install_coordinate_sampler()
    try:
        args = Namespace(
            startup_wait=18.0,
            hold=0.7,
            gap=0.25,
            interval=0.02,
            prelude="input-path",
            sequence=SEQUENCES,
            slot_source=SLOT_SOURCES,
            case_aliases=True,
            staged_kind="public source selector 0:0 coordinate load variants",
            prefix=DEFAULT_PREFIX,
            out_dir=OUT,
            output_prefix=OUTPUT_PREFIX,
        )
        summary = build_summary(args)
    finally:
        coord_poll.restore_coordinate_sampler()
    analysis = analyze(summary)
    summary["objective"] = "public source selector 0:0 load-only poll with coordinate and branch-state watches"
    summary["sourceSelector"] = SOURCE_SELECTOR
    summary["sourceStartTile"] = SOURCE_START_TILE
    summary["branchStateWatchValues"] = coord_poll.BRANCH_STATE_WATCH_VALUES
    summary["sourceLoadCoordinateAnalysis"] = analysis
    summary["promotionStatus"] = "blocked"
    summary["conclusion"] = (
        f"Staged public selector {SOURCE_SELECTOR} source saves and polled coordinate/branch-state watches across "
        f"{summary.get('sequenceCount')} load-only sequence(s). Classification={analysis.get('classification')}; "
        f"selector 2:0 reached={summary.get('anyReachedRouteSelectorContext')}. This remains non-promoting unless "
        "selector 2:0/current root or a strict source hotspot is observed."
    )
    coord_poll.prune_summary_for_output(summary)
    write_coordinate_outputs(summary)
    print(f"wrote source savedata load coordinate poll -> {OUT / (OUTPUT_PREFIX + '.html')}")


if __name__ == "__main__":
    main()
