"""
control_param_panel
PD / 2点パシュート制御パラメータ調整 UI パネル
"""

from PySide6.QtCore import Signal
from PySide6.QtWidgets import (
    QComboBox,
    QDoubleSpinBox,
    QFormLayout,
    QHBoxLayout,
    QInputDialog,
    QLabel,
    QMessageBox,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

from pc.gui.panels.collapsible_group_box import (
    CollapsibleGroupBox,
)

from pc.gui.panels.image_param_panel import _create_preset_ui
from pc.steering.param_store import (
    PdPreset,
    add_pd_preset,
    delete_pd_preset,
    load_pd_presets,
)
from pc.steering.pd_control import PdParams
from pc.steering.pursuit_control import PursuitParams


class ControlParamPanel(CollapsibleGroupBox):
    """PD / 2点パシュート制御パラメータ調整 UI"""

    # PD パラメータが変更されたときに emit する
    pd_params_changed = Signal(object)
    # Pursuit パラメータが変更されたときに emit する
    pursuit_params_changed = Signal(object)
    # 制御手法が変更されたときに emit する（"pd" or "pursuit"）
    steering_method_changed = Signal(str)

    def __init__(
        self,
        pd_params: PdParams,
        pursuit_params: PursuitParams,
        steering_method: str = "pd",
    ) -> None:
        super().__init__("制御パラメータ")
        self._pd_params = pd_params
        self._pursuit_params = pursuit_params
        self._initial_steering_method = steering_method
        self._auto_save_enabled = False
        self._pd_presets: list[PdPreset] = []

        self._setup_ui()
        self._auto_save_enabled = True

    def get_pd_params(self) -> PdParams:
        """現在の PD パラメータを返す"""
        return self._pd_params

    def get_pursuit_params(self) -> PursuitParams:
        """現在の Pursuit パラメータを返す"""
        return self._pursuit_params

    def _setup_ui(self) -> None:
        """UI を構築する"""
        layout = QVBoxLayout()
        self.setLayout(layout)

        # 制御手法の選択
        self._steering_combo = QComboBox()
        self._steering_combo.addItem("PD 制御", "pd")
        self._steering_combo.addItem(
            "2点パシュート", "pursuit",
        )
        idx = self._steering_combo.findData(
            self._initial_steering_method,
        )
        if idx >= 0:
            self._steering_combo.setCurrentIndex(idx)
        layout.addWidget(self._steering_combo)

        # --- PD パラメータ ---
        self._pd_param_form = QFormLayout()
        layout.addLayout(self._pd_param_form)

        p = self._pd_params

        self._spin_kp = _create_spin(p.kp, 0.0, 0.05)
        self._pd_param_form.addRow("Kp (位置):", self._spin_kp)

        self._spin_kh = _create_spin(p.kh, 0.0, 0.05)
        self._pd_param_form.addRow("Kh (傾き):", self._spin_kh)

        self._spin_kd = _create_spin(p.kd, 0.0, 0.05)
        self._pd_param_form.addRow("Kd (微分):", self._spin_kd)

        self._spin_max_steer_rate = _create_spin(
            p.max_steer_rate, 0.01, 0.01,
        )
        self._pd_param_form.addRow(
            "操舵制限:", self._spin_max_steer_rate,
        )

        self._spin_max_throttle = _create_spin(
            p.max_throttle, 0.0, 0.05,
        )
        self._pd_param_form.addRow(
            "最大速度:", self._spin_max_throttle,
        )

        self._spin_speed_k = _create_spin(
            p.speed_k, 0.0, 0.05,
        )
        self._pd_param_form.addRow(
            "減速係数:", self._spin_speed_k,
        )

        # PD 固有ウィジェットリスト（表示切替用）
        self._pd_widgets: list[QWidget] = [
            self._spin_kp,
            self._spin_kh,
            self._spin_kd,
        ]

        # --- Pursuit パラメータ ---
        self._pursuit_param_form = QFormLayout()
        layout.addLayout(self._pursuit_param_form)

        pp = self._pursuit_params

        self._spin_near_ratio = _create_spin(
            pp.near_ratio, 0.0, 0.05,
        )
        self._pursuit_param_form.addRow(
            "近目標(比率):", self._spin_near_ratio,
        )

        self._spin_far_ratio = _create_spin(
            pp.far_ratio, 0.0, 0.05,
        )
        self._pursuit_param_form.addRow(
            "遠目標(比率):", self._spin_far_ratio,
        )

        self._spin_k_near = _create_spin(
            pp.k_near, 0.0, 0.05,
        )
        self._pursuit_param_form.addRow(
            "K_near:", self._spin_k_near,
        )

        self._spin_k_far = _create_spin(
            pp.k_far, 0.0, 0.05,
        )
        self._pursuit_param_form.addRow(
            "K_far:", self._spin_k_far,
        )

        self._spin_pursuit_steer_rate = _create_spin(
            pp.max_steer_rate, 0.01, 0.01,
        )
        self._pursuit_param_form.addRow(
            "操舵制限:", self._spin_pursuit_steer_rate,
        )

        self._spin_pursuit_throttle = _create_spin(
            pp.max_throttle, 0.0, 0.05,
        )
        self._pursuit_param_form.addRow(
            "最大速度:", self._spin_pursuit_throttle,
        )

        self._spin_pursuit_speed_k = _create_spin(
            pp.speed_k, 0.0, 0.1,
        )
        self._pursuit_param_form.addRow(
            "減速係数:", self._spin_pursuit_speed_k,
        )

        # Pursuit 固有ウィジェットリスト（表示切替用）
        self._pursuit_widgets: list[QWidget] = [
            self._spin_near_ratio,
            self._spin_far_ratio,
            self._spin_k_near,
            self._spin_k_far,
            self._spin_pursuit_steer_rate,
            self._spin_pursuit_throttle,
            self._spin_pursuit_speed_k,
        ]

        # --- プリセット管理 ---
        self._pd_preset_combo, self._pd_preset_memo = (
            _create_preset_ui(
                layout,
                self._on_load_pd_preset,
                self._on_save_pd_preset,
                self._on_delete_pd_preset,
                self._on_pd_preset_selected,
            )
        )

        # コールバック接続
        self._steering_combo.currentIndexChanged.connect(
            self._on_steering_method_changed,
        )
        for spin in [
            self._spin_kp, self._spin_kh,
            self._spin_kd, self._spin_max_steer_rate,
            self._spin_max_throttle, self._spin_speed_k,
        ]:
            spin.valueChanged.connect(self._on_pd_changed)
        for spin in self._pursuit_widgets:
            spin.valueChanged.connect(
                self._on_pursuit_changed,
            )

        self._refresh_pd_presets()

        # 初期表示の更新
        self._on_steering_method_changed()

    def _on_pd_changed(self) -> None:
        """PD パラメータ SpinBox の値が変更されたときに反映する"""
        p = self._pd_params
        p.kp = self._spin_kp.value()
        p.kh = self._spin_kh.value()
        p.kd = self._spin_kd.value()
        p.max_steer_rate = self._spin_max_steer_rate.value()
        p.max_throttle = self._spin_max_throttle.value()
        p.speed_k = self._spin_speed_k.value()

        if self._auto_save_enabled:
            self.pd_params_changed.emit(p)

    def _on_pursuit_changed(self) -> None:
        """Pursuit パラメータの変更を反映する"""
        p = self._pursuit_params
        p.near_ratio = self._spin_near_ratio.value()
        p.far_ratio = self._spin_far_ratio.value()
        p.k_near = self._spin_k_near.value()
        p.k_far = self._spin_k_far.value()
        p.max_steer_rate = (
            self._spin_pursuit_steer_rate.value()
        )
        p.max_throttle = (
            self._spin_pursuit_throttle.value()
        )
        p.speed_k = self._spin_pursuit_speed_k.value()

        if self._auto_save_enabled:
            self.pursuit_params_changed.emit(p)

    def _on_steering_method_changed(self) -> None:
        """制御手法の変更を反映する"""
        method = self._steering_combo.currentData()
        is_pd = method == "pd"

        # PD 固有ウィジェットの表示切替
        for w in self._pd_widgets:
            w.setVisible(is_pd)
            label = self._pd_param_form.labelForField(w)
            if label:
                label.setVisible(is_pd)

        # 共通ウィジェット（操舵制限/最大速度/減速係数）
        for w in [
            self._spin_max_steer_rate,
            self._spin_max_throttle,
            self._spin_speed_k,
        ]:
            w.setVisible(is_pd)
            label = self._pd_param_form.labelForField(w)
            if label:
                label.setVisible(is_pd)

        # Pursuit ウィジェットの表示切替
        for w in self._pursuit_widgets:
            w.setVisible(not is_pd)
            label = (
                self._pursuit_param_form.labelForField(w)
            )
            if label:
                label.setVisible(not is_pd)

        if self._auto_save_enabled:
            self.steering_method_changed.emit(method)

    # ── PD プリセット管理 ──────────────────────────────────

    def _refresh_pd_presets(self) -> None:
        """PD 制御プリセット一覧を更新する"""
        self._pd_presets = load_pd_presets()
        self._pd_preset_combo.clear()
        for p in self._pd_presets:
            self._pd_preset_combo.addItem(p.title)
        self._pd_preset_memo.setText("")

    def _on_pd_preset_selected(self) -> None:
        """PD 制御プリセット選択時にメモを表示する"""
        idx = self._pd_preset_combo.currentIndex()
        if 0 <= idx < len(self._pd_presets):
            self._pd_preset_memo.setText(
                self._pd_presets[idx].memo,
            )
        else:
            self._pd_preset_memo.setText("")

    def _on_load_pd_preset(self) -> None:
        """PD 制御プリセットを読み込む"""
        idx = self._pd_preset_combo.currentIndex()
        if idx < 0 or idx >= len(self._pd_presets):
            return
        self._auto_save_enabled = False
        try:
            p = self._pd_presets[idx].params
            self._spin_kp.setValue(p.kp)
            self._spin_kh.setValue(p.kh)
            self._spin_kd.setValue(p.kd)
            self._spin_max_steer_rate.setValue(
                p.max_steer_rate,
            )
            self._spin_max_throttle.setValue(p.max_throttle)
            self._spin_speed_k.setValue(p.speed_k)
            self._pd_params = p
        finally:
            self._auto_save_enabled = True
        self.pd_params_changed.emit(p)

    def _on_save_pd_preset(self) -> None:
        """PD 制御プリセットを保存する"""
        title, ok = QInputDialog.getText(
            self, "PD プリセット保存", "タイトル:",
        )
        if not ok or not title.strip():
            return
        memo, ok = QInputDialog.getText(
            self, "PD プリセット保存", "メモ:",
        )
        if not ok:
            return
        p = self._pd_params
        add_pd_preset(PdPreset(
            title=title.strip(),
            memo=memo.strip(),
            params=PdParams(
                kp=p.kp, kh=p.kh, kd=p.kd,
                max_steer_rate=p.max_steer_rate,
                max_throttle=p.max_throttle,
                speed_k=p.speed_k,
            ),
        ))
        self._refresh_pd_presets()
        self._pd_preset_combo.setCurrentIndex(
            self._pd_preset_combo.count() - 1,
        )

    def _on_delete_pd_preset(self) -> None:
        """PD 制御プリセットを削除する"""
        idx = self._pd_preset_combo.currentIndex()
        if idx < 0 or idx >= len(self._pd_presets):
            return
        title = self._pd_presets[idx].title
        reply = QMessageBox.question(
            self, "削除確認",
            f"「{title}」を削除しますか？",
            QMessageBox.StandardButton.Yes
            | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No,
        )
        if reply == QMessageBox.StandardButton.Yes:
            delete_pd_preset(idx)
            self._refresh_pd_presets()


def _create_spin(
    value: float, min_val: float, step: float,
) -> QDoubleSpinBox:
    """パラメータ用の QDoubleSpinBox を作成する

    直接入力にも対応するため，上限は広めに設定する
    """
    spin = QDoubleSpinBox()
    spin.setRange(min_val, 99999.0)
    spin.setSingleStep(step)
    spin.setDecimals(3)
    spin.setValue(value)
    return spin
