"""Camera Settings dialog — sliders for UVC controls + Qt WhiteBalance/Exposure."""

from __future__ import annotations

import logging

from PySide6.QtCore import Qt
from PySide6.QtMultimedia import QCamera
from PySide6.QtWidgets import (
    QCheckBox,
    QComboBox,
    QDialog,
    QDialogButtonBox,
    QDoubleSpinBox,
    QFormLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QScrollArea,
    QSlider,
    QSpinBox,
    QVBoxLayout,
    QWidget,
)

from app.camera.uvc.base import UvcControllerBase, UvcParam

logger = logging.getLogger(__name__)

# Human-readable labels for each UVC parameter
_PARAM_LABELS: dict[UvcParam, str] = {
    UvcParam.BRIGHTNESS:             "Brightness",
    UvcParam.CONTRAST:               "Contrast",
    UvcParam.SATURATION:             "Saturation",
    UvcParam.HUE:                    "Hue",
    UvcParam.SHARPNESS:              "Sharpness",
    UvcParam.GAMMA:                  "Gamma",
    UvcParam.WHITE_BALANCE:          "White Balance (K)",
    UvcParam.BACKLIGHT_COMPENSATION: "Backlight Compensation",
    UvcParam.EXPOSURE:               "Exposure Time",
}

# Qt WhiteBalance modes shown in the combo
_WB_MODES: list[tuple[str, QCamera.WhiteBalanceMode]] = [
    ("Auto",        QCamera.WhiteBalanceMode.WhiteBalanceAuto),
    ("Sunlight",    QCamera.WhiteBalanceMode.WhiteBalanceSunlight),
    ("Cloudy",      QCamera.WhiteBalanceMode.WhiteBalanceCloudy),
    ("Shade",       QCamera.WhiteBalanceMode.WhiteBalanceShade),
    ("Tungsten",    QCamera.WhiteBalanceMode.WhiteBalanceTungsten),
    ("Fluorescent", QCamera.WhiteBalanceMode.WhiteBalanceFluorescent),
    ("Flash",       QCamera.WhiteBalanceMode.WhiteBalanceFlash),
    ("Sunset",      QCamera.WhiteBalanceMode.WhiteBalanceSunset),
    ("Manual (K)",  QCamera.WhiteBalanceMode.WhiteBalanceManual),
]

_EXPOSURE_MODES: list[tuple[str, QCamera.ExposureMode]] = [
    ("Auto",   QCamera.ExposureMode.ExposureAuto),
    ("Manual", QCamera.ExposureMode.ExposureManual),
]


class CameraSettingsDialog(QDialog):
    """
    Modal dialog for camera image controls.

    Sections:
      • Qt controls — WhiteBalance mode + colour temperature, Exposure mode + time
      • UVC controls — sliders for Brightness, Contrast, Saturation, Hue,
                       Sharpness, Gamma, White Balance (manual K), Backlight,
                       Exposure (if UVC controller is open)

    Changes are applied live to the camera.
    """

    def __init__(
        self,
        camera: QCamera,
        uvc: UvcControllerBase,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(parent)
        self.setWindowTitle("Camera Settings")
        self.setMinimumWidth(440)

        self._camera = camera
        self._uvc = uvc

        outer = QVBoxLayout(self)

        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        outer.addWidget(scroll)

        content = QWidget()
        scroll.setWidget(content)
        layout = QVBoxLayout(content)
        layout.setAlignment(Qt.AlignmentFlag.AlignTop)

        layout.addWidget(self._build_qt_group())

        uvc_group = self._build_uvc_group()
        if uvc_group is not None:
            layout.addWidget(uvc_group)

        buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
        buttons.rejected.connect(self.accept)
        outer.addWidget(buttons)

    # ------------------------------------------------------------------
    # Qt controls section
    # ------------------------------------------------------------------

    def _build_qt_group(self) -> QGroupBox:
        group = QGroupBox("Qt Camera Controls")
        form = QFormLayout(group)

        # White Balance mode
        self._wb_combo = QComboBox()
        for label, mode in _WB_MODES:
            self._wb_combo.addItem(label, mode)

        current_wb = self._camera.whiteBalanceMode()
        for i, (_, mode) in enumerate(_WB_MODES):
            if mode == current_wb:
                self._wb_combo.setCurrentIndex(i)
                break

        self._wb_combo.currentIndexChanged.connect(self._on_wb_mode_changed)
        form.addRow("White Balance:", self._wb_combo)

        # Colour temperature (manual only)
        self._temp_label = QLabel("Colour Temp (K):")
        self._temp_spin = QSpinBox()
        self._temp_spin.setRange(2000, 10000)
        self._temp_spin.setSingleStep(100)
        current_temp = self._camera.colorTemperature()
        self._temp_spin.setValue(current_temp if current_temp >= 2000 else 5500)
        self._temp_spin.valueChanged.connect(self._on_color_temp_changed)
        form.addRow(self._temp_label, self._temp_spin)
        self._update_temp_visibility()

        form.addRow(QLabel(""))  # spacer

        # Exposure mode
        self._exp_combo = QComboBox()
        for label, mode in _EXPOSURE_MODES:
            self._exp_combo.addItem(label, mode)

        current_exp = self._camera.exposureMode()
        for i, (_, mode) in enumerate(_EXPOSURE_MODES):
            if mode == current_exp:
                self._exp_combo.setCurrentIndex(i)
                break

        self._exp_combo.currentIndexChanged.connect(self._on_exp_mode_changed)
        form.addRow("Exposure Mode:", self._exp_combo)

        # Manual exposure time
        self._exp_label = QLabel("Exposure Time (s):")
        self._exp_spin = QDoubleSpinBox()
        exp_min = self._camera.minimumExposureTime()
        exp_max = self._camera.maximumExposureTime()
        self._exp_spin.setRange(
            exp_min if exp_min > 0 else 1e-6,
            exp_max if exp_max > 0 else 1.0,
        )
        self._exp_spin.setDecimals(6)
        self._exp_spin.setSingleStep(0.001)
        manual_t = self._camera.manualExposureTime()
        self._exp_spin.setValue(manual_t if manual_t > 0 else 0.033)
        self._exp_spin.valueChanged.connect(self._on_exp_time_changed)
        form.addRow(self._exp_label, self._exp_spin)
        self._update_exp_visibility()

        return group

    # ------------------------------------------------------------------
    # UVC controls section
    # ------------------------------------------------------------------

    def _build_uvc_group(self) -> QGroupBox | None:
        params = self._uvc.get_all_params()
        supported = [p for p in params if p.supported]

        group = QGroupBox("UVC Camera Controls")
        form = QFormLayout(group)

        if not supported:
            note = QLabel(
                "UVC controls not available.\n"
                "Install duvc-ctl (Windows) or pyuvc (macOS) to enable."
            )
            note.setEnabled(False)
            form.addRow(note)
            return group

        self._uvc_sliders: dict[UvcParam, QSlider] = {}
        self._uvc_spins: dict[UvcParam, QSpinBox] = {}
        self._uvc_auto_boxes: dict[UvcParam, QCheckBox] = {}

        for info in params:
            label = _PARAM_LABELS.get(info.param, info.param.name.title())

            if not info.supported:
                lbl = QLabel("Not supported")
                lbl.setEnabled(False)
                form.addRow(f"{label}:", lbl)
                continue

            row_widget = QWidget()
            row_layout = QHBoxLayout(row_widget)
            row_layout.setContentsMargins(0, 0, 0, 0)

            slider = QSlider(Qt.Orientation.Horizontal)
            slider.setRange(info.minimum, info.maximum)
            slider.setValue(info.current)
            slider.setSingleStep(max(1, info.step))

            spin = QSpinBox()
            spin.setRange(info.minimum, info.maximum)
            spin.setValue(info.current)
            spin.setFixedWidth(75)

            # Keep slider ↔ spin in sync
            slider.valueChanged.connect(spin.setValue)
            spin.valueChanged.connect(slider.setValue)
            slider.valueChanged.connect(
                lambda v, p=info.param: self._on_uvc_value(p, v)
            )

            row_layout.addWidget(slider)
            row_layout.addWidget(spin)

            if info.auto_supported:
                auto_box = QCheckBox("Auto")
                auto_box.setChecked(info.auto_enabled)
                auto_box.toggled.connect(
                    lambda checked, p=info.param: self._on_uvc_auto(p, checked)
                )
                row_layout.addWidget(auto_box)
                self._uvc_auto_boxes[info.param] = auto_box
                # Init enabled state
                slider.setEnabled(not info.auto_enabled)
                spin.setEnabled(not info.auto_enabled)

            self._uvc_sliders[info.param] = slider
            self._uvc_spins[info.param] = spin
            form.addRow(f"{label}:", row_widget)

        return group

    # ------------------------------------------------------------------
    # Qt control slots
    # ------------------------------------------------------------------

    def _on_wb_mode_changed(self, index: int) -> None:
        mode: QCamera.WhiteBalanceMode = self._wb_combo.itemData(index)
        if self._camera.isWhiteBalanceModeSupported(mode):
            self._camera.setWhiteBalanceMode(mode)
            logger.debug("WB mode: %s", mode)
        else:
            logger.debug("WB mode %s not supported by this camera", mode)
        self._update_temp_visibility()

    def _on_color_temp_changed(self, value: int) -> None:
        self._camera.setColorTemperature(value)
        logger.debug("Colour temp: %d K", value)

    def _on_exp_mode_changed(self, index: int) -> None:
        mode: QCamera.ExposureMode = self._exp_combo.itemData(index)
        if self._camera.isExposureModeSupported(mode):
            self._camera.setExposureMode(mode)
            logger.debug("Exposure mode: %s", mode)
        self._update_exp_visibility()

    def _on_exp_time_changed(self, value: float) -> None:
        self._camera.setManualExposureTime(value)
        logger.debug("Exposure time: %.6f s", value)

    def _update_temp_visibility(self) -> None:
        manual = (
            self._wb_combo.currentData()
            == QCamera.WhiteBalanceMode.WhiteBalanceManual
        )
        self._temp_label.setVisible(manual)
        self._temp_spin.setVisible(manual)

    def _update_exp_visibility(self) -> None:
        manual = (
            self._exp_combo.currentData()
            == QCamera.ExposureMode.ExposureManual
        )
        self._exp_label.setVisible(manual)
        self._exp_spin.setVisible(manual)

    # ------------------------------------------------------------------
    # UVC control slots
    # ------------------------------------------------------------------

    def _on_uvc_value(self, param: UvcParam, value: int) -> None:
        self._uvc.set_value(param, value)

    def _on_uvc_auto(self, param: UvcParam, enabled: bool) -> None:
        self._uvc.set_auto(param, enabled)
        slider = self._uvc_sliders.get(param)
        spin = self._uvc_spins.get(param)
        if slider:
            slider.setEnabled(not enabled)
        if spin:
            spin.setEnabled(not enabled)
