"""Preprocessing-set selection and sequential capture workflow mixin."""

from __future__ import annotations

from python_app.gui.preprocess_dialog import PreprocessDialog
from python_app.workflows.sequential_capture_workflow import SequentialCaptureSession


class AppWindowPreprocessMixin:
    """Handles calibration/reference set management and capture workflow."""

    def _open_preprocess_panel(self) -> None:
        """Open preprocessing dialog and refresh available sets."""
        dialog = self._ensure_preprocess_dialog()
        try:
            self._refresh_sets()
            self._update_capture_dialog_state()
        except Exception as exc:  # noqa: BLE001
            self._show_error(f"Failed to open preprocessing panel: {exc}")
            return

        dialog.show()
        dialog.raise_()
        dialog.activateWindow()

    def _ensure_preprocess_dialog(self) -> PreprocessDialog:
        """Create preprocessing dialog lazily and wire its signals once."""
        if self._preprocess_dialog is not None:
            return self._preprocess_dialog

        dialog = PreprocessDialog(self)
        dialog.refresh_requested.connect(self._refresh_sets)
        dialog.selection_changed.connect(self._on_preprocess_selection_changed)
        dialog.start_sequence_requested.connect(self._start_capture_sequence)
        dialog.capture_next_requested.connect(self._capture_next_combo)
        dialog.abort_sequence_requested.connect(self._abort_capture_sequence)
        self._preprocess_dialog = dialog
        self._update_capture_dialog_state()
        return dialog

    def _on_preprocess_selection_changed(self, calibration_set: str, reference_set: str) -> None:
        """Persist selected preprocessing set names from dialog."""
        self._selected_calibration_set = calibration_set.strip()
        self._selected_reference_set = reference_set.strip()
        self._refresh_preprocess_summary_labels()

    def _refresh_preprocess_summary_labels(self) -> None:
        """Update compact summary labels in the main window."""
        self._selected_calibration_label.setText(self._selected_calibration_set or "<not selected>")
        self._selected_reference_label.setText(self._selected_reference_set or "<not selected>")

    def _refresh_sets(self) -> None:
        """Refresh calibration/reference set lists for current radar key."""
        config = self._build_config()
        radar_key = self._radar_key(config)
        calibration_sets = self._store.list_sets("calibration", radar_key)
        reference_sets = self._store.list_sets("reference", radar_key)

        dialog = self._ensure_preprocess_dialog()
        dialog.set_calibration_sets(calibration_sets)
        dialog.set_reference_sets(reference_sets)

        if self._selected_calibration_set not in calibration_sets:
            self._selected_calibration_set = calibration_sets[0] if calibration_sets else ""
        if self._selected_reference_set not in reference_sets:
            self._selected_reference_set = reference_sets[0] if reference_sets else ""

        dialog.set_selected_sets(self._selected_calibration_set, self._selected_reference_set)
        self._refresh_preprocess_summary_labels()
        self._log(f"Set lists refreshed for key={radar_key}")

    def _start_capture_sequence(self, kind: str) -> None:
        """Start sequential capture session for requested preprocessing kind."""
        if self._capture_session is not None:
            self._show_error("Another capture sequence is already active")
            return

        dialog = self._ensure_preprocess_dialog()
        set_name = dialog.set_name()
        if not set_name:
            self._show_error("Set name is required")
            return

        was_running = self._supervisor.is_running()
        if was_running:
            self._log("Pipeline paused for exclusive hardware capture")
            self._stop_run()

        self._resume_pipeline_after_capture = was_running

        try:
            config = self._build_config()
            radar_key = self._radar_key(config)
            existing_sets = self._store.list_sets(kind, radar_key)
            if set_name in existing_sets:
                raise RuntimeError(f"Set '{set_name}' already exists for {kind} and cannot be overwritten")

            session = SequentialCaptureSession(config=config, kind=kind, set_name=set_name)
            session.open()
            self._capture_session = session

            dialog.clear_capture_log()
            dialog.set_status(f"{kind.title()} sequence started")
            self._update_capture_dialog_state()
            self._log(f"{kind.title()} sequence started for set={set_name}; fill all N*M combos")
        except Exception as exc:  # noqa: BLE001
            self._cleanup_capture_session()
            self._show_error(f"Failed to start {kind} sequence: {exc}")
            self._resume_pipeline_if_needed()

    def _capture_next_combo(self) -> None:
        """Capture next combo in active sequential capture session."""
        session = self._capture_session
        if session is None:
            self._show_error("No active capture sequence")
            return

        dialog = self._ensure_preprocess_dialog()

        try:
            trace = session.capture_current_combo()
            state = session.state()
            tx_label, rx_label = dialog.antenna_labels()

            dialog.append_capture_log_entry(
                kind=session.kind,
                captured_count=state.captured_count,
                total_count=state.total_count,
                input_pos=trace.combo.input_pos,
                output_pos=trace.combo.output_pos,
                tx_label=tx_label,
                rx_label=rx_label,
            )

            dialog.draw_last_trace(trace, title=f"{session.kind.title()} captured")
            self._draw_single_trace(trace, title=f"{session.kind.title()} last trace")

            self._log(
                f"{session.kind.title()} capture: {state.captured_count}/{state.total_count} | "
                f"input={trace.combo.input_pos} output={trace.combo.output_pos}"
            )

            if session.is_complete():
                radar_key, collection = session.finalize(self._store)
                set_name = session.set_name
                kind = session.kind
                self._cleanup_capture_session()

                if kind == "calibration":
                    self._selected_calibration_set = set_name
                else:
                    self._selected_reference_set = set_name

                self._refresh_sets()
                dialog.set_status(f"{kind.title()} set saved: {set_name} ({len(collection.traces)} traces)")
                self._log(f"{kind.title()} sequence completed and saved: set={set_name}, key={radar_key}")
                self._resume_pipeline_if_needed()
            else:
                self._update_capture_dialog_state()
        except Exception as exc:  # noqa: BLE001
            self._show_error(f"Failed to capture combo: {exc}")
            self._abort_capture_sequence()

    def _abort_capture_sequence(self, *, resume_pipeline: bool = True) -> None:
        """Abort active capture session and optionally resume pipeline."""
        if self._capture_session is None:
            return

        kind = self._capture_session.kind
        self._cleanup_capture_session()
        dialog = self._ensure_preprocess_dialog()
        dialog.set_status(f"{kind.title()} sequence aborted")
        self._log(f"{kind.title()} sequence aborted")
        if resume_pipeline:
            self._resume_pipeline_if_needed()

    def _update_capture_dialog_state(self) -> None:
        """Sync dialog state widgets with active capture session."""
        if self._preprocess_dialog is None:
            return

        if self._capture_session is None:
            self._preprocess_dialog.set_capture_state(
                kind=None,
                captured_count=0,
                total_count=0,
                next_input=None,
                next_output=None,
            )
            return

        state = self._capture_session.state()
        next_input = None
        next_output = None
        if state.current_combo is not None:
            next_input = state.current_combo.input
            next_output = state.current_combo.output

        self._preprocess_dialog.set_capture_state(
            kind=state.kind,
            captured_count=state.captured_count,
            total_count=state.total_count,
            next_input=next_input,
            next_output=next_output,
        )

    def _cleanup_capture_session(self) -> None:
        """Close and clear current capture session object."""
        if self._capture_session is not None:
            self._capture_session.close()
            self._capture_session = None
        self._update_capture_dialog_state()

    def _resume_pipeline_if_needed(self) -> None:
        """Resume acquisition pipeline if it was paused for capture session."""
        should_resume = self._resume_pipeline_after_capture
        self._resume_pipeline_after_capture = False
        if not should_resume:
            return

        try:
            self._start_run()
        except Exception as exc:  # noqa: BLE001
            self._show_error(f"Failed to resume pipeline after capture: {exc}")
