# -*- coding: utf-8 -*-
# gui/components/color_palette.py
"""Палитра выбора цвета — контейнеризованный компонент."""

from __future__ import annotations

from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QGridLayout, QPushButton, QSizePolicy, QWidget

from gui.containers.s_container import SContainer

# Стандартная палитра 32 цвета (4×8).
DEFAULT_PALETTE_COLORS: list[str] = [
    "#FF6B6B", "#FF8E72", "#FFB26B", "#FFD56B", "#F0E96B", "#B8E86B", "#7EDB79", "#5CC8A8",
    "#59B6E6", "#5F9DFF", "#7A84FF", "#A07CFF", "#C27BFF", "#E07ADB", "#F57FB1", "#D97A5C",
    "#B35A4A", "#8E4A3D", "#6F5A4D", "#7E7E7E", "#5C5C5C", "#3F4A5A", "#2F6F8F", "#2F8F86",
    "#3C8F52", "#6D8F3C", "#8F873C", "#8F6B3C", "#8F4F3C", "#7B3C8F", "#4F3C8F", "#3C5C8F",
]


class ColorPalette(SContainer):
    """Сетка цветных кнопок с единственным выделением.

    Сигнал ``color_selected`` испускается при клике.
    Публичный API — ``set_color_hex``, ``get_color_hex``, ``set_enabled``.
    """

    color_selected = Signal(str)  # #RRGGBB

    def __init__(
        self,
        colors: list[str] | None = None,
        columns: int = 8,
        selected_index: int = 0,
        width_percent: int | None = None,
        height_percent: int | None = None,
        margin: int | tuple[int, int, int, int] = 0,
        parent: QWidget | None = None,
    ):
        super().__init__(
            width_percent=width_percent,
            height_percent=height_percent,
            margin=margin,
            parent=parent,
        )
        self._colors: list[str] = list(colors or DEFAULT_PALETTE_COLORS)
        self._columns = max(1, int(columns))
        self._selected_index: int = max(0, min(int(selected_index), len(self._colors) - 1))
        self._buttons: list[QPushButton] = []

        # Внутренний виджет с QGridLayout — палитра динамическая,
        # GridContainer не подходит (ячейки без авторасширения QPushButton).
        self._grid_widget = QWidget()
        self._grid_layout = QGridLayout(self._grid_widget)
        self._grid_layout.setContentsMargins(0, 0, 0, 0)
        self._grid_layout.setHorizontalSpacing(4)
        self._grid_layout.setVerticalSpacing(4)
        super().add_widget(self._grid_widget)

        self._build_grid()

    # ── Публичный API ─────────────────────────────────────────────

    def get_color_hex(self) -> str:
        """Вернуть выбранный цвет ``#RRGGBB``."""
        if not self._colors:
            return "#7FB3D5"
        idx = max(0, min(self._selected_index, len(self._colors) - 1))
        return str(self._colors[idx])

    def set_color_hex(self, color_hex: str) -> None:
        """Выбрать ближайший цвет палитры к ``color_hex``."""
        idx = self._nearest_index(str(color_hex or ""))
        if idx == self._selected_index:
            return
        self._selected_index = idx
        self._refresh_selection()

    def get_selected_index(self) -> int:
        """Индекс выбранного цвета."""
        return self._selected_index

    def set_selected_index(self, index: int) -> None:
        """Выбрать цвет по индексу."""
        index = max(0, min(int(index), len(self._colors) - 1))
        if index == self._selected_index:
            return
        self._selected_index = index
        self._refresh_selection()

    def set_enabled(self, enabled: bool) -> None:
        """Включить/выключить все кнопки палитры."""
        for button in self._buttons:
            button.setEnabled(bool(enabled))
        super().set_enabled(enabled)

    def add_widget(self, widget, alignment=None):
        raise NotImplementedError("ColorPalette is a sealed component")

    # ── Внутренние методы ─────────────────────────────────────────

    def _build_grid(self) -> None:
        self._buttons = []
        for idx, color in enumerate(self._colors):
            button = QPushButton("")
            button.setCheckable(True)
            button.setCursor(Qt.CursorShape.PointingHandCursor)
            button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
            button.clicked.connect(lambda _checked=False, i=idx: self._on_clicked(i))
            self._buttons.append(button)
            row = idx // self._columns
            col = idx % self._columns
            self._grid_layout.addWidget(button, row, col)
            self._apply_button_style(idx, selected=(idx == self._selected_index))
        self._refresh_selection()

    def _on_clicked(self, index: int) -> None:
        index = max(0, min(int(index), len(self._colors) - 1))
        self._selected_index = index
        self._refresh_selection()
        self.color_selected.emit(self.get_color_hex())

    def _refresh_selection(self) -> None:
        for idx, button in enumerate(self._buttons):
            is_selected = idx == self._selected_index
            button.blockSignals(True)
            button.setChecked(is_selected)
            button.blockSignals(False)
            self._apply_button_style(idx, selected=is_selected)

    def _apply_button_style(self, index: int, selected: bool) -> None:
        if index < 0 or index >= len(self._buttons):
            return
        color = self._colors[index]
        border_color = "#FFFFFF" if selected else "#5F5F5F"
        border_width = 2 if selected else 1
        self._buttons[index].setStyleSheet(
            f"QPushButton {{"
            f"background-color: {color};"
            f"border: {border_width}px solid {border_color};"
            f"border-radius: 2px;"
            f"min-height: 18px;"
            f"}}"
            f"QPushButton:disabled {{"
            f"background-color: {color};"
            f"border: {border_width}px solid #3A3A3A;"
            f"}}"
        )

    def _nearest_index(self, color_hex: str) -> int:
        if not self._colors:
            return 0
        target = QColor(color_hex)
        if not target.isValid():
            return 0
        best_idx = 0
        best_dist: int | None = None
        for idx, candidate_hex in enumerate(self._colors):
            candidate = QColor(candidate_hex)
            dr = int(target.red()) - int(candidate.red())
            dg = int(target.green()) - int(candidate.green())
            db = int(target.blue()) - int(candidate.blue())
            dist = dr * dr + dg * dg + db * db
            if best_dist is None or dist < best_dist:
                best_dist = dist
                best_idx = idx
        return best_idx


# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
#
# 1) Назначение модуля:
#    Палитра выбора цвета — сетка цветных кнопок (по умолчанию 4×8)
#    с единственным выделением и сигналом color_selected, встроенная
#    в контейнер SContainer для процентного позиционирования.
#
# 2) Зависимости модуля:
#    Импорты: Qt, Signal (PySide6.QtCore),
#             QColor (PySide6.QtGui),
#             QGridLayout, QPushButton, QSizePolicy, QWidget (PySide6.QtWidgets),
#             SContainer (gui.containers.s_container)
#    Хост-класс / базовый класс: SContainer
#    Внешние библиотеки: PySide6 (обязательна)
#
# 3) Экспорт:
#    Константа DEFAULT_PALETTE_COLORS (list[str], 32 цвета).
#    Класс ColorPalette — публичный компонент палитры.
#    Сигнал: color_selected(str) — #RRGGBB при клике.
#    Методы: get_color_hex(), set_color_hex(), get_selected_index(),
#            set_selected_index(), set_enabled().
#
# 4) Состояние (поля):
#    _colors: list[str]         — массив HEX-цветов палитры
#    _columns: int              — количество столбцов сетки
#    _selected_index: int       — индекс текущего выбранного цвета
#    _buttons: list[QPushButton]— массив кнопок-ячеек
#    _grid_widget: QWidget      — внутренний виджет с QGridLayout
#    _grid_layout: QGridLayout  — компоновка сетки
#
# 5) Последовательность действий и вызовов:
#    __init__(colors?, columns, selected_index, ...)
#      -> super().__init__(...)
#      -> _grid_widget + QGridLayout — контейнер для кнопок
#      -> super().add_widget(_grid_widget)
#      -> _build_grid() — создание QPushButton для каждого цвета
#         -> для каждого цвета: QPushButton -> setCheckable -> connect(_on_clicked)
#         -> _apply_button_style(idx, selected)
#      -> _refresh_selection()
#    set_color_hex(color_hex)
#      -> _nearest_index(color_hex) — поиск ближайшего по евклидову расстоянию RGB
#      -> _refresh_selection() — обновление всех кнопок
#    _on_clicked(index)
#      -> _refresh_selection() -> color_selected.emit(get_color_hex())
#
# 6) Побочные эффекты:
#    - Устанавливает inline stylesheet на каждую кнопку палитры.
#    - Испускает сигнал color_selected при выборе цвета.
#
# 7) Границы ответственности:
#    Модуль НЕ подключается к theme_bus (стили инлайновые).
#    НЕ хранит «применённый» цвет зоны — только текущий выбор палитры.
#    add_widget() заблокирован — компонент запечатан.
#
# 8) Обработка ошибок:
#    add_widget() бросает NotImplementedError. _nearest_index() возвращает 0
#    при невалидном HEX. Индексы зажимаются в допустимый диапазон (clamp).
#
# 9) Инварианты и контракты:
#    - 0 ≤ _selected_index < len(_colors).
#    - Ровно одна кнопка в checked-состоянии (exclusive selection).
#    - Количество кнопок == len(_colors); _columns ≥ 1.
#
# 10) Правило сопровождения:
#     Для изменения набора цветов — менять DEFAULT_PALETTE_COLORS или
#     передавать colors в конструктор. Не изменять логику _nearest_index
#     без учёта perceptual color distance.
