#!/usr/bin/env python3
"""Poll map1_01a exit paths after a source-save load sequence that observes selector 0:0."""
from __future__ import annotations

from argparse import Namespace
from typing import Any

import probe_runtime_input_path as runtime_input
import probe_runtime_predecessor_coordinate_branch_state_poll as coord_poll
import probe_runtime_source_exit_branch_state_poll as source_exit
from probe_runtime_input_path import DEFAULT_PREFIX, OUT, ROOT
from probe_runtime_selected_pointer_multislot_savedata_poll import build_summary
from probe_runtime_selected_pointer_poll import write_outputs
from summarize_map_tiles import load_maps


OUTPUT_PREFIX = "runtime_selected_pointer_source_exit_branch_state_load_confirmed_poll"

# A direct load-variant poll observes public selector 0:0 with this prefix, while
# the earlier Down,Return-only source-exit poll stayed in selector 50:0.
LOAD_PREFIX = ["Down", "Return", "Return"]
WARMUP_SEQUENCE = "source-save-menu-warmup=Down,Return"


def planned_sequences() -> tuple[list[str], list[dict[str, Any]]]:
    maps = load_maps(ROOT / "out" / "maps.js")
    info = maps[source_exit.SOURCE_MAP]
    sequences: list[str] = [WARMUP_SEQUENCE]
    plans: list[dict[str, Any]] = []
    for candidate in source_exit.SOURCE_EXIT_CANDIDATES:
        sequence_name = f"{candidate['name']}-load-confirmed"
        path_keys = coord_poll.find_path(info, source_exit.START_TILE, candidate["candidateTile"])
        keys = [*LOAD_PREFIX, *path_keys, *candidate["tailKeys"]]
        sequences.append(f"{sequence_name}={','.join(keys)}")
        plans.append({
            **candidate,
            "name": sequence_name,
            "baseName": candidate["name"],
            "sourceMap": source_exit.SOURCE_MAP,
            "targetMap": source_exit.TARGET_MAP,
            "startTile": source_exit.START_TILE,
            "outsideTile": source_exit.outside_tile(candidate),
            "loadPrefix": LOAD_PREFIX,
            "pathStepCount": len(path_keys),
            "pathRunLengths": coord_poll.run_lengths(path_keys),
            "pathKeys": path_keys,
            "pathPoints": coord_poll.path_points(source_exit.START_TILE, path_keys),
            "sequenceKeyCount": len(keys),
            "sequenceKeys": keys,
        })
    return sequences, plans


def write_source_exit_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") + source_exit.source_exit_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)
    sequences, plans = planned_sequences()
    args = Namespace(
        startup_wait=18.0,
        hold=0.7,
        gap=0.25,
        interval=0.02,
        prelude="input-path",
        sequence=sequences,
        slot_source=[f"1={source_exit.SOURCE_SAVE}"],
        case_aliases=True,
        staged_kind="public source selector 0:0 load-confirmed exit branch-state watch",
        prefix=DEFAULT_PREFIX,
        out_dir=OUT,
        output_prefix=OUTPUT_PREFIX,
    )
    coord_poll.install_coordinate_sampler()
    try:
        summary = build_summary(args)
    finally:
        coord_poll.restore_coordinate_sampler()
    analysis = source_exit.analyze(summary, plans)
    summary["objective"] = "public source selector 0:0 map1_01a exit path poll with load-confirmed prefix"
    summary["sourceSave"] = source_exit.SOURCE_SAVE
    summary["sourceMap"] = source_exit.SOURCE_MAP
    summary["targetMap"] = source_exit.TARGET_MAP
    summary["startTile"] = source_exit.START_TILE
    summary["loadPrefix"] = LOAD_PREFIX
    summary["targetedExitCandidates"] = plans
    summary["branchStateWatchValues"] = coord_poll.BRANCH_STATE_WATCH_VALUES
    summary["coordinateWatchModel"] = {
        "cameraTileGlobalsHex": [coord_poll.hex32(coord_poll.CAMERA_TILE_X), coord_poll.hex32(coord_poll.CAMERA_TILE_Y)],
        "activeActorPointerTableHex": coord_poll.hex32(coord_poll.ACTOR_POINTER_TABLE),
        "actorObjectTileOffsetsHex": [coord_poll.hex32(coord_poll.OBJECT_TILE_X_OFFSET), coord_poll.hex32(coord_poll.OBJECT_TILE_Y_OFFSET)],
        "partyTrailRingHex": {
            "historyIndexTable": coord_poll.hex32(coord_poll.ACTOR_HISTORY_INDEX_TABLE),
            "seedTileXTable": coord_poll.hex32(coord_poll.ACTOR_SEED_X_TABLE),
            "seedTileYTable": coord_poll.hex32(coord_poll.ACTOR_SEED_Y_TABLE),
            "slotCount": coord_poll.ACTOR_HISTORY_SLOT_COUNT,
            "strideBytes": coord_poll.ACTOR_HISTORY_STRIDE_BYTES,
        },
    }
    summary["sourceExitAnalysis"] = analysis
    summary["sourceExitBranchStateSplit"] = {
        "classification": analysis.get("classification"),
        "anyCandidateObserved": analysis.get("anyCandidateObserved"),
        "anyOutsideObserved": analysis.get("anyOutsideObserved"),
        "anyActorOrTrailCandidateObserved": analysis.get("anyActorOrTrailCandidateObserved"),
        "anyActorOrTrailOutsideObserved": analysis.get("anyActorOrTrailOutsideObserved"),
        "anyReachedCurrentRoot": analysis.get("anyReachedCurrentRoot"),
        "anyReachedRouteSelectorContext": analysis.get("anyReachedRouteSelectorContext"),
    }
    summary["promotionStatus"] = "blocked"
    summary["conclusion"] = (
        f"Staged public selector 0:0 source save and replayed the load-confirmed prefix "
        f"{','.join(LOAD_PREFIX)} before {summary.get('sequenceCount')} map1_01a exit path(s). "
        f"Source exit classification={analysis.get('classification')}; "
        f"selector 2:0 reached={summary.get('anyReachedRouteSelectorContext')}. "
        "This is non-promoting unless selector 2:0/current root or a strict source hotspot is observed."
    )
    coord_poll.prune_summary_for_output(summary)
    write_source_exit_outputs(summary)
    print(f"wrote load-confirmed source exit branch-state poll -> {OUT / (OUTPUT_PREFIX + '.html')}")


if __name__ == "__main__":
    main()
