# -*- coding: utf-8 -*-
# gui/components/coordinate_input.py
"""Виджет для ввода координат"""

from PySide6.QtWidgets import QDoubleSpinBox, QSizePolicy
from PySide6.QtCore import Qt, Slot

from gui.containers.s_container import SContainer
from gui.styles import APP_STYLES
from gui.theme_bus import theme_bus
from error_logger import log_exception


class CoordinateInput(SContainer):
    """Виджет для ввода координат с валидацией"""
    
    def __init__(
        self,
        min_value: float = 0.0,
        max_value: float = 100000.0,
        decimals: int = 6,
        step: float = 0.000001,
        min_width: int = 150,
        alignment: Qt.Alignment = Qt.AlignCenter,
        parent=None,
        style: str = "COORDINATE_INPUT",
        active_style: str | None = None,
        is_active: bool | None = None,
    ):
        super().__init__(
            width_percent=None,
            height_percent=None,
            margin=0,
            style=style,
            active_style=active_style,
            is_active=is_active,
            parent=parent,
        )
        
        self._theme = "dark"
        self._is_active = False
        self._base_style_key = style
        self._style_key_normal = None
        self._style_key_active = None
        
        self._input = QDoubleSpinBox()
        self._input.setRange(min_value, max_value)
        self._input.setDecimals(decimals)
        self._input.setSingleStep(step)
        self._input.setMinimumWidth(min_width)
        self._input.setAlignment(alignment)
        self._input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
        super().add_widget(self._input)
        
        if active_style is not None:
            self._style_key_normal = style
            self._style_key_active = active_style
        if is_active is not None:
            self._is_active = bool(is_active)

        self.style()
        theme_bus.theme_changed.connect(self.set_theme)
        
    def style(
        self,
        style_key: str | None = None,
        active_key: str | None = None,
        is_active: bool | None = None,
    ) -> None:
        """Короткий метод применения стиля. Можно задать ключи и активность явно."""
        if style_key is not None:
            self._base_style_key = style_key
            if active_key is not None:
                self._style_key_normal = style_key
                self._style_key_active = active_key
            else:
                self._style_key_normal = None
                self._style_key_active = None

        if is_active is not None:
            self._is_active = bool(is_active)

        if self._style_key_normal is not None:
            active_key = self._style_key_active or self._style_key_normal
            key = active_key if self._is_active else self._style_key_normal
            themed = f"{key}_{self._theme.upper()}"
            if themed in APP_STYLES:
                key = themed
            self._input.setStyleSheet(APP_STYLES.get(key, ""))
            return

        base_key = self._base_style_key
        key = base_key

        if self._theme == "light":
            if self._is_active and f"{base_key}_LIGHT_ACTIVE" in APP_STYLES:
                key = f"{base_key}_LIGHT_ACTIVE"
            elif f"{base_key}_LIGHT" in APP_STYLES:
                key = f"{base_key}_LIGHT"
        else:
            if self._is_active and f"{base_key}_DARK_ACTIVE" in APP_STYLES:
                key = f"{base_key}_DARK_ACTIVE"
            elif f"{base_key}_DARK" in APP_STYLES:
                key = f"{base_key}_DARK"

        self._input.setStyleSheet(APP_STYLES.get(key, ""))

    @Slot(str)
    def set_theme(self, theme: str) -> None:
        """Внешний слот: принимает 'dark' или 'light'."""
        theme = (theme or "").strip().lower()
        if theme not in ("dark", "light"):
            return
        if self._theme == theme:
            return
        self._theme = theme
        self.style()
        
    def set_prefix(self, prefix: str):
        """Установка префикса"""
        self._input.setPrefix(prefix)
        
    def set_range(self, min_val, max_val):
        """Установка диапазона"""
        self._input.setRange(min_val, max_val)
        
    def set_decimals(self, decimals: int):
        """Установка количества десятичных знаков"""
        self._input.setDecimals(decimals)

    def set_step(self, step: float) -> None:
        """Установка шага"""
        self._input.setSingleStep(step)
        
    def set_value(self, value):
        """Безопасная установка значения"""
        try:
            self._input.setValue(float(value))
        except (ValueError, TypeError) as _exc:
            log_exception(__name__, "set_value", _exc)
    def get_value(self):
        return self._input.value()

    @property
    def valueChanged(self):
        """Предоставить сигнал valueChanged из внутреннего QDoubleSpinBox."""
        return self._input.valueChanged

    def set_enabled(self, enabled: bool) -> None:
        self._input.setEnabled(enabled)
        super().setEnabled(enabled)

    def set_min_width(self, width: int) -> None:
        self._input.setMinimumWidth(width)

    def set_min_height(self, height: int) -> None:
        self._input.setMinimumHeight(height)

    def set_max_width(self, width: int) -> None:
        self._input.setMaximumWidth(width)

    def set_max_height(self, height: int) -> None:
        self._input.setMaximumHeight(height)

    def set_fixed_size(self, width: int, height: int) -> None:
        self._input.setMinimumSize(width, height)
        self._input.setMaximumSize(width, height)

    def set_tooltip(self, text: str) -> None:
        self._input.setToolTip(text)

    def set_size_policy(self, horizontal, vertical) -> None:
        self._input.setSizePolicy(horizontal, vertical)
        super().setSizePolicy(horizontal, vertical)

    def add_widget(self, widget, alignment=None):
        raise NotImplementedError("CoordinateInput can contain only one QDoubleSpinBox")




# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
#
# 1) Назначение модуля:
#    Виджет для ввода координат (широта, долгота и т.д.) на базе
#    QDoubleSpinBox внутри SContainer, с поддержкой валидации
#    диапазона, настраиваемой точностью и стилями APP_STYLES.
#
# 2) Зависимости модуля:
#    Импорты: QDoubleSpinBox, QSizePolicy (PySide6.QtWidgets),
#             Qt, Slot (PySide6.QtCore),
#             SContainer (gui.containers.s_container),
#             APP_STYLES (gui.styles),
#             theme_bus (gui.theme_bus)
#    Хост-класс / базовый класс: SContainer
#    Внешние библиотеки: PySide6 (обязательна)
#
# 3) Экспорт:
#    Класс CoordinateInput — публичный виджет ввода координат.
#    Методы: style(), set_theme(), set_prefix(), set_range(),
#            set_decimals(), set_step(), set_value(), get_value(),
#            set_enabled(), set_min/max_width/height(), set_fixed_size(),
#            set_tooltip(), set_size_policy().
#    Свойство: valueChanged — сигнал QDoubleSpinBox.valueChanged.
#
# 4) Состояние (поля):
#    _theme: str                 — текущая тема
#    _is_active: bool            — признак активного состояния
#    _base_style_key: str        — базовый ключ стиля ("COORDINATE_INPUT")
#    _style_key_normal: str|None — явный ключ нормального стиля
#    _style_key_active: str|None — явный ключ активного стиля
#    _input: QDoubleSpinBox      — внутренний виджет ввода
#
# 5) Последовательность действий и вызовов:
#    __init__(min_value, max_value, decimals, step, min_width, alignment, ...)
#      -> super().__init__(...)
#      -> QDoubleSpinBox() с setRange, setDecimals, setSingleStep, setMinimumWidth
#      -> super().add_widget(_input)
#      -> style() -> theme_bus.theme_changed.connect(set_theme)
#    set_value(value)
#      -> try float(value) -> _input.setValue()
#      -> except: pass (тихое игнорирование)
#    valueChanged (property)
#      -> делегирует к _input.valueChanged
#
# 6) Побочные эффекты:
#    - Устанавливает stylesheet на QDoubleSpinBox.
#    - Подключается к theme_bus.theme_changed.
#
# 7) Границы ответственности:
#    Модуль НЕ интерпретирует значения координат семантически.
#    НЕ выполняет геокодирование. add_widget() заблокирован.
#
# 8) Обработка ошибок:
#    set_value() глотает ValueError/TypeError при некорректном вводе.
#    add_widget() бросает NotImplementedError.
#    set_theme() молча игнорирует невалидные темы.
#
# 9) Инварианты и контракты:
#    - Контейнер содержит ровно один QDoubleSpinBox.
#    - Значение всегда в пределах [min_value, max_value].
#    - decimals определяет точность отображения.
#
# 10) Правило сопровождения:
#     При добавлении суффикса/префикса — использовать set_prefix().
#     Стили — через APP_STYLES с ключом COORDINATE_INPUT + суффиксы.
