"""Helpers for GUI-side runtime history management."""

from __future__ import annotations

from collections import deque
from typing import TypeVar

from python_app.models.dataset_model import ResultCollection, SweepCollection
from python_app.models.run_config_model import RunConfigModel

THistoryCollection = TypeVar("THistoryCollection", SweepCollection, ResultCollection)


def record_result_history(
    result_history: deque[ResultCollection],
    collection: ResultCollection,
) -> bool:
    """Append new result or replace existing entry by stable collection key."""
    for index in range(len(result_history) - 1, -1, -1):
        existing = result_history[index]
        if (
            existing.collection_id == collection.collection_id
            and existing.monotonic_ns == collection.monotonic_ns
        ):
            result_history[index] = collection
            return True

    result_history.append(collection)
    return True


def remove_last_aligned_histories(
    raw_history: list[SweepCollection],
    preprocessed_history: list[SweepCollection],
    result_history: list[ResultCollection],
) -> tuple[list[SweepCollection], list[SweepCollection], list[ResultCollection]]:
    """Remove the newest aligned history entry using results as preferred anchor."""
    retained_raw = list(raw_history)
    retained_preprocessed = list(preprocessed_history)
    retained_results = list(result_history)

    if retained_results:
        target_key = _tail_occurrence_key(retained_results, len(retained_results) - 1)
        retained_results.pop()
        _remove_by_tail_occurrence_key(retained_raw, target_key)
        _remove_by_tail_occurrence_key(retained_preprocessed, target_key)
        return retained_raw, retained_preprocessed, retained_results

    if retained_preprocessed:
        target_key = _tail_occurrence_key(retained_preprocessed, len(retained_preprocessed) - 1)
        retained_preprocessed.pop()
        _remove_by_tail_occurrence_key(retained_raw, target_key)
        return retained_raw, retained_preprocessed, retained_results

    if retained_raw:
        retained_raw.pop()

    return retained_raw, retained_preprocessed, retained_results


def build_run_history_signature(
    config: RunConfigModel,
) -> tuple[object, ...]:
    """Build deterministic signature to detect run-settings changes (excluding live processing params)."""
    combos_signature = tuple((int(combo.input), int(combo.output)) for combo in config.combos)
    return (
        str(config.radar.driver_mode),
        str(config.radar.serial),
        float(config.radar.sweep.start_hz),
        float(config.radar.sweep.stop_hz),
        int(config.radar.sweep.points),
        float(config.radar.sweep.if_bandwidth_hz),
        float(config.radar.sweep.power_dbm),
        str(config.input_switch.driver_mode),
        str(config.input_switch.driver),
        int(config.input_switch.positions),
        bool(config.input_switch.invert_logic),
        str(config.output_switch.driver_mode),
        str(config.output_switch.driver),
        int(config.output_switch.positions),
        bool(config.output_switch.invert_logic),
        str(config.preprocess.calibration_set),
        str(config.preprocess.reference_set),
        combos_signature,
    )


def _tail_occurrence_key(history: list[THistoryCollection], index: int) -> tuple[int, int]:
    """Return `(collection_id, occurrence_from_tail)` for the item at `index`."""
    collection_id = int(history[index].collection_id)
    occurrence_from_tail = 0
    for cursor in range(len(history) - 1, index, -1):
        if int(history[cursor].collection_id) == collection_id:
            occurrence_from_tail += 1
    return collection_id, occurrence_from_tail


def _remove_by_tail_occurrence_key(history: list[THistoryCollection], key: tuple[int, int]) -> bool:
    """Remove newest matching entry identified by `(collection_id, occurrence_from_tail)`."""
    collection_id, target_occurrence = key
    occurrence_from_tail = 0
    for index in range(len(history) - 1, -1, -1):
        if int(history[index].collection_id) != collection_id:
            continue
        if occurrence_from_tail == target_occurrence:
            history.pop(index)
            return True
        occurrence_from_tail += 1
    return False
