# -*- coding: utf-8 -*-
# gui/components/topology_tree_widget.py
"""
Виджет дерева топологии склада с ленивой загрузкой.
"""

from __future__ import annotations

from typing import Optional, Dict, Any, Callable
from dataclasses import dataclass

from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QHeaderView, QSizePolicy
from PySide6.QtGui import QFont

from gui.containers.s_container import SContainer
from gui.styles import APP_STYLES
from gui.theme_bus import theme_bus
from gui.components._tree_node_building import TreeNodeBuilder
from gui.components._tree_state_management import TreeStateManager


@dataclass
class TreeNodeData:
    """Данные узла дерева."""
    node_type: str  # типы узлов: 'site', 'facility', 'zone', 'rack', 'shelf', 'cell', 'volume'
    node_id: str  # UUID или другой идентификатор
    display_data: Dict[str, Any]  # Данные для отображения
    has_children: bool = False
    children_loaded: bool = False
    raw_data: Optional[Dict[str, Any]] = None  # Полные данные узла


class TopologyTreeWidget(SContainer):
    """Виджет дерева топологии склада с ленивой загрузкой."""

    # Сигналы
    nodeSelected = Signal(str, str, dict)  # тип, ид, данные отображения
    nodeDoubleClicked = Signal(str, str, dict)  # тип, ид, данные отображения
    dataLoadError = Signal(str)  # сообщение об ошибке

    # Карта отображения атрибутов для каждого типа узла (только ключи)
    DISPLAY_ATTRIBUTES = {
        'site': ['code', 'site_city'],
        'facility': ['facility_type'],
        'zone': ['code', 'name'],
        'rack': ['code', 'name'],
        'shelf': ['code', 'name', 'params', 'size'],
        'cell': ['r', 'c', 'z'],
        'volume': ['code', 'volume_mode', 'max_w', 'max_h', 'max_d']
    }

    def __init__(
        self,
        data_loader: Callable[[str, Optional[str]], list[TreeNodeData]],
        width_percent: int | None = None,
        height_percent: int | None = None,
        margin: int | tuple[int, int, int, int] = 0,
        style: str = "TREE_WIDGET_TOP_FLAT",
        parent=None
    ):
        super().__init__(
            width_percent=width_percent,
            height_percent=height_percent,
            margin=margin,
            parent=parent,
            style=style,
        )

        self.data_loader = data_loader
        self._loading_nodes = set()
        self._tree = None
        self._tree_style_key = style

        # Сервисы (композиция вместо mixin)
        self._node_builder = TreeNodeBuilder(self)
        self._state_mgr = TreeStateManager(self)

        self._tree = QTreeWidget()
        self._tree.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
        super().add_widget(self._tree)
        self._apply_tree_style()

        self._setup_ui()
        self._connect_signals()
        theme_bus.theme_changed.connect(self._apply_tree_style)

        QTimer.singleShot(0, self._node_builder.load_root_nodes)

    # -- Настройка интерфейса -------------------------------------------------------------

    def _setup_ui(self) -> None:
        """Настройка внешнего вида и поведения виджета."""
        self._tree.setColumnCount(2)
        self._tree.setHeaderHidden(True)

        header = self._tree.header()
        header.setStretchLastSection(True)
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.Stretch)

        self._tree.setSelectionMode(QAbstractItemView.SingleSelection)
        self._tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self._tree.setAnimated(True)
        self._tree.setUniformRowHeights(True)
        self._tree.setExpandsOnDoubleClick(False)
        self._tree.setItemsExpandable(True)

        font = QFont()
        font.setPointSize(10)
        self._tree.setFont(font)

    def _apply_tree_style(self, *_args) -> None:
        """Явно применяем стиль к QTreeWidget."""
        if not self._tree_style_key:
            return
        if self._tree is None:
            return
        self._tree.setStyleSheet(APP_STYLES.get(self._tree_style_key, ""))

    def _connect_signals(self) -> None:
        """Подключение внутренних сигналов."""
        self._tree.itemClicked.connect(self._on_item_clicked)
        self._tree.itemDoubleClicked.connect(self._on_item_double_clicked)
        self._tree.itemExpanded.connect(self._on_item_expanded)
        self._tree.itemCollapsed.connect(self._on_item_collapsed)

    # -- Обработчики событий -------------------------------------------------------

    def _on_item_clicked(self, item: QTreeWidgetItem, column: int) -> None:
        """Обработчик одинарного клика на элементе дерева."""
        item_data = item.data(0, Qt.UserRole)
        if not item_data:
            return
        if item_data.get('is_stub') or item_data.get('is_error'):
            return
        self.nodeSelected.emit(item_data['type'], item_data['id'], item_data['display_data'])

    def _on_item_double_clicked(self, item: QTreeWidgetItem, column: int) -> None:
        """Обработчик двойного клика на элементе дерева."""
        item_data = item.data(0, Qt.UserRole)
        if not item_data:
            return
        if item_data.get('is_stub') or item_data.get('is_error'):
            return
        self.nodeDoubleClicked.emit(item_data['type'], item_data['id'], item_data['display_data'])

    def _on_item_expanded(self, item: QTreeWidgetItem) -> None:
        """Обработчик раскрытия элемента дерева."""
        item_data = item.data(0, Qt.UserRole)
        if not item_data:
            return
        if item_data.get('has_children') and not item_data.get('children_loaded'):
            self._node_builder.load_children(item)
        self.resizeColumnToContents(0)

    def _on_item_collapsed(self, item: QTreeWidgetItem) -> None:
        """Обработчик сворачивания элемента дерева."""
        item_data = item.data(0, Qt.UserRole)
        if not item_data:
            return

    # -- Методы-адаптеры QWidget ----------------------------------------------

    def __getattr__(self, name: str):
        if name == "_tree":
            return None
        tree = self.__dict__.get("_tree")
        if tree is None:
            return super().__getattribute__(name)
        return getattr(tree, name)

    def viewport(self):
        if self._tree is None:
            return None
        return self._tree.viewport()

    def setMouseTracking(self, enabled: bool) -> None:
        super().setMouseTracking(enabled)
        if self._tree is not None:
            self._tree.setMouseTracking(enabled)
            viewport = self._tree.viewport()
            if viewport is not None:
                viewport.setMouseTracking(enabled)

    def installEventFilter(self, filter_obj) -> None:
        super().installEventFilter(filter_obj)
        if self._tree is not None:
            self._tree.installEventFilter(filter_obj)
            viewport = self._tree.viewport()
            if viewport is not None:
                viewport.installEventFilter(filter_obj)

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

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

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

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

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

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

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

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

    # -- Делегирование к сервисам (публичный API) ----------------------------

    def reload_tree(self, preserve_state: bool = True) -> None:
        self._state_mgr.reload_tree(preserve_state)

    def clear_tree(self) -> None:
        self._state_mgr.clear_tree()

    def refresh_node(self, node_type: str, node_id: str) -> None:
        self._state_mgr.refresh_node(node_type, node_id)

    def select_node(
        self,
        node_type: str,
        node_id: str,
        *,
        emit_selected: bool = False,
        allow_load: bool = True,
        expand_parents: bool = True,
    ) -> bool:
        return self._state_mgr.select_node(
            node_type, node_id,
            emit_selected=emit_selected,
            allow_load=allow_load,
            expand_parents=expand_parents,
        )
