# -*- coding: utf-8 -*-
# gui/containers/scroll_container.py
"""Прокручиваемый контейнер с процентным sizing и контейнерным API проекта."""

from __future__ import annotations

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QFrame, QLayout, QScrollArea, QVBoxLayout, QWidget

from .content_host import ContentHost
from .percent_sized_widget import PercentSizedWidget


class ScrollContainer(PercentSizedWidget):
    """Контейнер-обёртка над QScrollArea с поддержкой percent sizing."""

    def __init__(
        self,
        width_percent: int | float | None = None,
        height_percent: int | float | None = None,
        margin: int | tuple[int, int, int, int] = 0,
        content_margins: int | tuple[int, int, int, int] = 0,
        spacing: int = 0,
        orientation: str = "v",
        widget_resizable: bool = True,
        vertical_scroll_bar_policy: str | Qt.ScrollBarPolicy = "as_needed",
        horizontal_scroll_bar_policy: str | Qt.ScrollBarPolicy = "always_off",
        content_fit: bool = False,
        parent: QWidget | None = None,
        style: str | None = None,
        active_style: str | None = None,
        is_active: bool | None = None,
    ):
        super().__init__(width_percent, height_percent, parent)
        self._init_stylable()
        self._auto_add_children = True

        self._scroll_area = QScrollArea(self)
        self._scroll_area.setWidgetResizable(bool(widget_resizable))
        self._scroll_area.setFrameShape(QFrame.Shape.NoFrame)

        self._content_host = ContentHost(
            orientation=orientation,
            margin=content_margins,
            spacing=spacing,
            parent=self,
        )
        self._content_host.set_content_fit(bool(content_fit))
        self._content_host.get_layout().setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize)
        self._scroll_area.setWidget(self._content_host)

        self._layout: QVBoxLayout = QVBoxLayout(self)
        self._layout.setSpacing(0)
        self.set_margins(margin)
        self._layout.addWidget(self._scroll_area)

        self.set_vertical_scroll_bar_policy(vertical_scroll_bar_policy)
        self.set_horizontal_scroll_bar_policy(horizontal_scroll_bar_policy)

        if style is not None or active_style is not None or is_active is not None:
            self._apply_style(style_key=style, active_key=active_style, is_active=is_active)

    @staticmethod
    def _normalize_scroll_policy(policy: str | Qt.ScrollBarPolicy) -> Qt.ScrollBarPolicy:
        if isinstance(policy, Qt.ScrollBarPolicy):
            return policy
        token = str(policy or "").strip().lower()
        mapping = {
            "as_needed": Qt.ScrollBarPolicy.ScrollBarAsNeeded,
            "always_off": Qt.ScrollBarPolicy.ScrollBarAlwaysOff,
            "always_on": Qt.ScrollBarPolicy.ScrollBarAlwaysOn,
        }
        if token not in mapping:
            raise ValueError(
                "Unknown scroll policy. Allowed: 'as_needed', 'always_off', 'always_on'."
            )
        return mapping[token]

    def add_widget(self, widget: QWidget, alignment=None) -> None:
        self._content_host.add_widget(widget)

    def add_widget_with_stretch(self, widget: QWidget, stretch: int, alignment=None) -> None:
        self._content_host.add_widget_with_stretch(widget, stretch)

    def add_stretch(self, stretch: int = 1) -> None:
        self._content_host.add_stretch(stretch)

    def invalidate_layout(self) -> None:
        self._content_host.get_layout().invalidate()

    def get_layout(self) -> QLayout:
        return self._layout

    def set_margins(self, margin: int | tuple[int, int, int, int]) -> None:
        if isinstance(margin, (list, tuple)) and len(margin) == 4:
            self._layout.setContentsMargins(*margin)
        else:
            self._layout.setContentsMargins(margin, margin, margin, margin)

    def set_content_margins(self, margin: int | tuple[int, int, int, int]) -> None:
        layout = self._content_host.get_layout()
        if isinstance(margin, (list, tuple)) and len(margin) == 4:
            layout.setContentsMargins(*margin)
        else:
            layout.setContentsMargins(margin, margin, margin, margin)

    def set_spacing(self, spacing: int) -> None:
        self._content_host.get_layout().setSpacing(spacing)

    def set_widget_resizable(self, enabled: bool) -> None:
        self._scroll_area.setWidgetResizable(bool(enabled))

    def set_vertical_scroll_bar_policy(self, policy: str | Qt.ScrollBarPolicy) -> None:
        self._scroll_area.setVerticalScrollBarPolicy(self._normalize_scroll_policy(policy))

    def set_horizontal_scroll_bar_policy(self, policy: str | Qt.ScrollBarPolicy) -> None:
        self._scroll_area.setHorizontalScrollBarPolicy(self._normalize_scroll_policy(policy))

    @property
    def scroll_area(self) -> QScrollArea:
        return self._scroll_area


# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
#
# 1) Назначение модуля:
#    Прокручиваемый контейнер — обёртка над QScrollArea с поддержкой
#    процентного sizing и контейнерного API проекта (add_widget, стилизация,
#    тема). Используется для длинных списков, форм и панелей, которые
#    не помещаются в видимую область.
#
# 2) Зависимости модуля:
#    Импорты: Qt, QFrame, QLayout, QScrollArea, QVBoxLayout, QWidget (PySide6)
#    Хост/базовый класс: StylableMixin + PercentSizedWidget (MRO)
#    Внутренние: ContentHost (content_host.py), StylableMixin (stylable_mixin.py)
#    Внешние библиотеки: PySide6
#
# 3) Экспорт:
#    Класс ScrollContainer — прокручиваемый контейнер.
#    Методы: add_widget(), add_widget_with_stretch(), add_stretch(),
#            invalidate_layout(), get_layout(), set_margins(),
#            set_content_margins(), set_spacing(), set_widget_resizable(),
#            set_vertical_scroll_bar_policy(), set_horizontal_scroll_bar_policy()
#    Свойство: scroll_area (доступ к QScrollArea).
#
# 4) Состояние (поля):
#    _scroll_area     : QScrollArea — Qt scroll area (NoFrame).
#    _content_host    : ContentHost — внутренний хост с layout для потомков.
#    _layout          : QVBoxLayout — внешний layout самого контейнера.
#    _auto_add_children: bool = True — потомки авто-добавляются.
#
# 5) Последовательность действий и вызовов:
#    __init__(params) -> super().__init__(w%, h%, parent)
#      -> _init_stylable() -> создание QScrollArea (NoFrame)
#      -> создание ContentHost(orientation, content_margins, spacing)
#      -> _content_host.set_content_fit(content_fit)
#      -> _content_host.get_layout().setSizeConstraint(SetMinAndMaxSize)
#      -> _scroll_area.setWidget(_content_host)
#      -> создание _layout (QVBoxLayout) -> _layout.addWidget(_scroll_area)
#      -> set_vertical/horizontal_scroll_bar_policy()
#      -> _apply_style() если style задан
#    add_widget(w) -> _content_host.add_widget(w)
#
# 6) Побочные эффекты:
#    ContentHost помещается внутрь QScrollArea как scrollable widget.
#    SizeConstraint = SetMinAndMaxSize — контролирует поведение scroll.
#    _auto_add_children = True — дочерние PercentSizedWidget авто-добавляются.
#    _apply_style() устанавливает stylesheet на self.
#
# 7) Границы ответственности:
#    НЕ управляет содержимым скролла — это делает ContentHost.
#    НЕ реализует собственный scroll — делегирует QScrollArea.
#    НЕ применяет alignment/springs.
#
# 8) Обработка ошибок:
#    _normalize_scroll_policy: ValueError при невалидной строке политики.
#    Допустимые значения: "as_needed", "always_off", "always_on".
#
# 9) Инварианты и контракты:
#    - scroll_bar_policy ∈ {"as_needed", "always_off", "always_on"} или
#      Qt.ScrollBarPolicy enum.
#    - По умолчанию vertical = as_needed, horizontal = always_off.
#    - content_fit по умолчанию False (в отличие от других контейнеров).
#    - widget_resizable по умолчанию True.
#
# 10) Правило сопровождения:
#     При изменении scroll-политик проверять комбинацию с content_fit
#     и widget_resizable — они взаимозависимы. SizeConstraint
#     (SetMinAndMaxSize) критичен для правильного поведения scroll.
