"""Validation helpers for GUI processing mode constraints."""

from __future__ import annotations

from python_app.models.run_config_model import RunConfigModel
from python_app.orchestration.live_processing_config import ProcessingLiveConfig


def validate_processing_mode_constraints(
    processing_mode: str,
    config: RunConfigModel,
    live_config: ProcessingLiveConfig,
) -> None:
    """Validate mode-specific constraints for current run configuration."""
    if processing_mode == "bscan":
        any_native_switch = config.input_switch.driver_mode == "native" or config.output_switch.driver_mode == "native"
        if not any_native_switch:
            return

        combo_count = len({(int(combo.input), int(combo.output)) for combo in config.combos})
        if combo_count != 1:
            raise RuntimeError(
                f"B-scan with native switches requires exactly one run combo (now {combo_count})"
            )
        return

    if processing_mode not in {"gpr", "legacy_gpr"}:
        return

    configured_combos = {(int(combo.input), int(combo.output)) for combo in config.combos}
    if len(configured_combos) < 2:
        raise RuntimeError(f"GPR requires at least 2 distinct run combos (now {len(configured_combos)})")

    available_inputs = sorted({int(entry.input_pos) for entry in config.gpr.rx_geometry})
    available_outputs = sorted({int(entry.output_pos) for entry in config.gpr.tx_geometry})
    if not available_inputs or not available_outputs:
        raise RuntimeError("GPR requires non-empty Tx/Rx geometry in run_config")

    requested_inputs = sorted({int(value) for value in live_config.gpr_input_positions})
    requested_outputs = sorted({int(value) for value in live_config.gpr_output_positions})

    selected_inputs = requested_inputs or available_inputs
    selected_outputs = requested_outputs or available_outputs

    missing_inputs = [value for value in selected_inputs if value not in available_inputs]
    missing_outputs = [value for value in selected_outputs if value not in available_outputs]
    if missing_inputs:
        raise RuntimeError(f"GPR input positions are missing from geometry config: {missing_inputs}")
    if missing_outputs:
        raise RuntimeError(f"GPR output positions are missing from geometry config: {missing_outputs}")

    required_combos = {(int(input_pos), int(output_pos)) for input_pos in selected_inputs for output_pos in selected_outputs}
    missing_combos = sorted(required_combos - configured_combos)
    if missing_combos:
        raise RuntimeError(
            "GPR run combos do not cover selected input/output positions: "
            f"{missing_combos}"
        )
