"""Dialog for calibration/reference set selection and sequential capture."""

from __future__ import annotations

from PyQt6.QtCore import pyqtSignal
from PyQt6.QtWidgets import (
    QComboBox,
    QDialog,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPlainTextEdit,
    QPushButton,
    QVBoxLayout,
)
import pyqtgraph as pg
import numpy as np

from python_app.models.dataset_model import TraceData


class PreprocessDialog(QDialog):
    """Standalone dialog for preprocessing capture workflows.

    The dialog combines three concerns:
    1. Set selection (calibration/reference).
    2. Sequential capture controls for filling all N*M combinations.
    3. Quick preview of the last captured trace.
    """

    refresh_requested = pyqtSignal()
    selection_changed = pyqtSignal(str, str)
    start_sequence_requested = pyqtSignal(str)
    capture_next_requested = pyqtSignal()
    abort_sequence_requested = pyqtSignal()

    def __init__(self, parent=None) -> None:
        """Initialize window metadata and compose dialog UI."""
        super().__init__(parent)
        self._init_window()
        self._build_ui()

    def _init_window(self) -> None:
        """Set static window properties."""
        self.setWindowTitle("Preprocessing Setup")
        self.resize(1040, 760)

    def _build_ui(self) -> None:
        """Build root dialog layout and all sections."""
        root_layout = QVBoxLayout(self)
        root_layout.addWidget(self._build_sets_group())
        root_layout.addWidget(self._build_sequence_group())
        self._build_status_line(root_layout)
        self._build_preview_plot(root_layout)

    def _build_sets_group(self) -> QGroupBox:
        """Build set-management controls used for preprocessing snapshots."""
        group = QGroupBox("Calibration / Reference Sets", self)
        layout = QGridLayout(group)

        self._set_name_input = QLineEdit("set_001", group)
        self._calibration_combo = QComboBox(group)
        self._reference_combo = QComboBox(group)

        refresh_button = QPushButton("Refresh Sets", group)
        refresh_button.clicked.connect(self.refresh_requested.emit)

        self._calibration_combo.currentTextChanged.connect(self._emit_selection_changed)
        self._reference_combo.currentTextChanged.connect(self._emit_selection_changed)

        layout.addWidget(QLabel("Set name"), 0, 0)
        layout.addWidget(self._set_name_input, 0, 1)
        layout.addWidget(refresh_button, 0, 2)

        layout.addWidget(QLabel("Calibration set"), 1, 0)
        layout.addWidget(self._calibration_combo, 1, 1, 1, 2)

        layout.addWidget(QLabel("Reference set"), 2, 0)
        layout.addWidget(self._reference_combo, 2, 1, 1, 2)
        return group

    def _build_sequence_group(self) -> QGroupBox:
        """Build sequential-capture controls and capture log."""
        group = QGroupBox("Sequential Capture (Fill Full N*M)", self)
        layout = QGridLayout(group)

        self._active_kind_label = QLabel("<none>", group)
        self._progress_label = QLabel("0 / 0", group)
        self._combo_label = QLabel("<none>", group)

        self._tx_antenna_label_input = QLineEdit(group)
        self._rx_antenna_label_input = QLineEdit(group)
        self._tx_antenna_label_input.setPlaceholderText("e.g. TX_A")
        self._rx_antenna_label_input.setPlaceholderText("e.g. RX_B")

        button_row = self._build_sequence_button_row(group)

        layout.addWidget(QLabel("Active type"), 0, 0)
        layout.addWidget(self._active_kind_label, 0, 1)
        layout.addWidget(QLabel("Progress"), 1, 0)
        layout.addWidget(self._progress_label, 1, 1)
        layout.addWidget(QLabel("Current combo"), 2, 0)
        layout.addWidget(self._combo_label, 2, 1)
        layout.addWidget(QLabel("TX antenna label"), 3, 0)
        layout.addWidget(self._tx_antenna_label_input, 3, 1)
        layout.addWidget(QLabel("RX antenna label"), 4, 0)
        layout.addWidget(self._rx_antenna_label_input, 4, 1)
        layout.addLayout(button_row, 5, 0, 1, 2)

        self._capture_log = QPlainTextEdit(group)
        self._capture_log.setReadOnly(True)
        self._capture_log.setPlaceholderText("Capture history per combo")
        layout.addWidget(self._capture_log, 6, 0, 1, 2)
        return group

    def _build_sequence_button_row(self, parent: QGroupBox) -> QHBoxLayout:
        """Build action buttons for sequence flow control."""
        start_calibration_button = QPushButton("Start Calibration Sequence", parent)
        start_calibration_button.clicked.connect(lambda: self.start_sequence_requested.emit("calibration"))

        start_reference_button = QPushButton("Start Reference Sequence", parent)
        start_reference_button.clicked.connect(lambda: self.start_sequence_requested.emit("reference"))

        self._capture_next_button = QPushButton("Capture Current Combo", parent)
        self._capture_next_button.clicked.connect(self.capture_next_requested.emit)
        self._capture_next_button.setEnabled(False)

        self._abort_button = QPushButton("Abort Sequence", parent)
        self._abort_button.clicked.connect(self.abort_sequence_requested.emit)
        self._abort_button.setEnabled(False)

        button_row = QHBoxLayout()
        button_row.addWidget(start_calibration_button)
        button_row.addWidget(start_reference_button)
        button_row.addWidget(self._capture_next_button)
        button_row.addWidget(self._abort_button)
        return button_row

    def _build_status_line(self, root_layout: QVBoxLayout) -> None:
        """Build one-line status output for dialog operations."""
        self._status_label = QLabel("Ready", self)
        root_layout.addWidget(self._status_label)

    def _build_preview_plot(self, root_layout: QVBoxLayout) -> None:
        """Build trace preview plot used after each successful capture."""
        self._preview_plot = pg.PlotWidget(background="#101418")
        self._preview_plot.showGrid(x=True, y=True, alpha=0.2)
        self._preview_plot.setLabel("bottom", "Frequency", units="Hz")
        self._preview_plot.setLabel("left", "Magnitude", units="dB")
        root_layout.addWidget(self._preview_plot, stretch=1)

    def set_name(self) -> str:
        """Return requested target set name."""
        return self._set_name_input.text().strip()

    def calibration_set(self) -> str:
        """Return currently selected calibration set."""
        return self._calibration_combo.currentText().strip()

    def reference_set(self) -> str:
        """Return currently selected reference set."""
        return self._reference_combo.currentText().strip()

    def antenna_labels(self) -> tuple[str, str]:
        """Return optional TX/RX user labels used in capture logs."""
        return self._tx_antenna_label_input.text().strip(), self._rx_antenna_label_input.text().strip()

    def clear_capture_log(self) -> None:
        """Clear capture history text box."""
        self._capture_log.clear()

    def append_capture_log_entry(
        self,
        *,
        kind: str,
        captured_count: int,
        total_count: int,
        input_pos: int,
        output_pos: int,
        tx_label: str,
        rx_label: str,
    ) -> None:
        """Append one capture progress row to dialog log."""
        tx_info = tx_label or "-"
        rx_info = rx_label or "-"
        self._capture_log.appendPlainText(
            f"{kind}: {captured_count}/{total_count} | "
            f"input={input_pos} output={output_pos} | "
            f"TX={tx_info} RX={rx_info}"
        )

    def set_capture_state(
        self,
        *,
        kind: str | None,
        captured_count: int,
        total_count: int,
        next_input: int | None,
        next_output: int | None,
    ) -> None:
        """Update sequence progress/status widgets."""
        if kind is None:
            self._active_kind_label.setText("<none>")
            self._progress_label.setText("0 / 0")
            self._combo_label.setText("<none>")
            self._capture_next_button.setEnabled(False)
            self._abort_button.setEnabled(False)
            return

        self._active_kind_label.setText(kind)
        self._progress_label.setText(f"{captured_count} / {total_count}")
        if next_input is None or next_output is None:
            self._combo_label.setText("<complete>")
            self._capture_next_button.setEnabled(False)
            self._abort_button.setEnabled(True)
        else:
            self._combo_label.setText(f"input={next_input}, output={next_output}")
            self._capture_next_button.setEnabled(True)
            self._abort_button.setEnabled(True)

    def set_calibration_sets(self, names: list[str]) -> None:
        """Replace calibration set choices while preserving current selection when possible."""
        self._set_combo_items(self._calibration_combo, names, self.calibration_set())

    def set_reference_sets(self, names: list[str]) -> None:
        """Replace reference set choices while preserving current selection when possible."""
        self._set_combo_items(self._reference_combo, names, self.reference_set())

    def set_selected_sets(self, calibration_set: str, reference_set: str) -> None:
        """Apply selected set names to both comboboxes and emit selection update."""
        if calibration_set:
            index = self._calibration_combo.findText(calibration_set)
            if index >= 0:
                self._calibration_combo.setCurrentIndex(index)

        if reference_set:
            index = self._reference_combo.findText(reference_set)
            if index >= 0:
                self._reference_combo.setCurrentIndex(index)

        self._emit_selection_changed()

    def set_status(self, message: str) -> None:
        """Set short human-readable status line."""
        self._status_label.setText(message)

    def draw_last_trace(self, trace: TraceData, title: str) -> None:
        """Draw the latest captured sweep trace in dB scale."""
        magnitude_db = 20.0 * np.log10(np.maximum(np.abs(trace.s21), 1e-12))
        self._preview_plot.clear()
        self._preview_plot.plot(
            trace.frequency_hz,
            magnitude_db,
            pen=pg.mkPen("#4cc9f0", width=1.8),
        )
        combo = trace.combo
        self._status_label.setText(
            f"{title}: input={combo.input_pos}, output={combo.output_pos}, points={trace.frequency_hz.size}"
        )

    def _emit_selection_changed(self) -> None:
        """Emit current calibration/reference selection."""
        self.selection_changed.emit(self.calibration_set(), self.reference_set())

    @staticmethod
    def _set_combo_items(combo: QComboBox, names: list[str], current_text: str) -> None:
        """Replace combo contents and keep previous value when still available."""
        combo.clear()
        combo.addItems(names)
        if not current_text:
            return
        index = combo.findText(current_text)
        if index >= 0:
            combo.setCurrentIndex(index)
