# -*- coding: utf-8 -*-
# gui/components/model_view_widget.py
"""Виджет отображения и управления 3D-моделью с поддержкой зон.

Данный файл использует фасад composition-слоя `gui.components.model_view.contour`
и миксины с функционалом, которые внедрены в `ModelViewWidget`.
"""

from typing import Optional, Tuple, Callable

from gui.containers import VContainer, SContainer
from PySide6.QtCore import Signal
from gui.theme_bus import theme_bus
from error_logger import log_exception

from gui.components.model_view._interaction_scenario import InteractionManager
from gui.components.model_view._scenario_camera import CameraScenario
from gui.components.model_view._scenario_facility_browse import FacilityBrowseScenario
from gui.components.model_view.contour import (
    ModelViewContourFacade,
    LegacyContourBindingComponent,
)

from gui.components.model_view import (
    ModelLoadingMixin,
    ZoneManagementMixin,
    InteractionMixin,
    VisualHelpersMixin,
    GridCoreMixin,
    DimensionLinesMixin,
    RackPlacementMixin,
    ScenePresentationMixin,
    SceneModesMixin,
    RackCameraTransitionMixin,
    ZoneCameraTransitionMixin,
    RackPlacementIOMixin,
)


class ModelViewWidget(
    InteractionMixin,
    ModelLoadingMixin,
    ZoneManagementMixin,
    VisualHelpersMixin,
    GridCoreMixin,
    DimensionLinesMixin,
    RackPlacementMixin,
    ScenePresentationMixin,
    SceneModesMixin,
    RackCameraTransitionMixin,
    ZoneCameraTransitionMixin,
    RackPlacementIOMixin,
    SContainer,
):
    """Виджет для отображения 3D моделей помещения и зон."""

    # Сигналы
    error_occurred = Signal(str)
    zone_selected = Signal(str)  # ид. зоны
    zone_double_clicked = Signal(str)  # двойной ЛКМ по зоне
    camera_mode_changed = Signal(bool)  # True = заблокирована
    grid_volume_changed = Signal(float, float, float)  # ширина, глубина, высота
    contour_ready_changed = Signal(bool)  # контур ортогонален и пригоден для объёма
    rack_layout_changed = Signal(str)  # zone_id
    rack_slot_visibility_changed = Signal(str, str, bool)  # rack_id, slot_id, occupied
    rack_selected_changed = Signal(str)  # ид. стеллажа или ""
    rack_double_clicked = Signal(str, str)  # rack_id, zone_id
    shelf_slot_selected = Signal(str, str)  # rack_id, slot_id
    shelf_slot_double_clicked = Signal(str, str, int)  # rack_id, slot_id, shelf_index

    def __init__(
        self,
        width_percent: int | None = None,
        height_percent: int | None = None,
        margin: int | tuple[int, int, int, int] = 0,
        content_fit: bool = True,
        parent=None,
        style: str | None = None,
        active_style: str | None = None,
        is_active: bool | None = None,
    ):
        super().__init__(
            width_percent=width_percent,
            height_percent=height_percent,
            content_fit=content_fit,
            parent=parent,
            style=style,
            active_style=active_style,
            is_active=is_active,
        )
        self._content_margin = margin
        self._content_fit = content_fit
        self._plotter = None
        self._models_loaded = False
        self._camera_locked = False
        self._theme = "dark"

        # Данные моделей
        self._floor_mesh = None
        self._walls_mesh = None
        self._ceiling_mesh = None
        self._truss_mesh = None
        self._rack_meshes: list = []
        self._zones: dict = {}  # ид. зоны -> mesh
        self._zone_data: dict = {}  # zone_id -> (min_x, max_x, min_y, max_y, min_z, max_z)
        self._zone_polygons: dict = {}  # ид. зоны -> list[(x, y)]
        self._zone_heights: dict = {}  # zone_id -> (start_z, height)
        self._model_actors: dict = {}  # ключ -> actor
        self._missing_models: set = set()

        # Интерактивное размещение
        self._preview_zone = None
        self._current_zone_size = 100  # мм
        self._current_zone_z_size = 100  # мм, шаг по высоте Z
        self._current_zone_color = "#FF6B6B80"

        # Ограничивающий параллелепипед
        self._room_bounds = None  # (min_x, max_x, min_y, max_y, min_z, max_z)

        # Режим выбора зон с сеткой
        self._zone_selection_mode = False
        self._zone_pick_enabled = True
        self._selected_zone_highlight_id: Optional[str] = None
        self._selected_zone_original_opacity: Optional[float] = None
        self._selected_zone_original_visibility: Optional[bool] = None
        self._hover_highlighted_zone_id: Optional[str] = None
        self._hover_zone_original_opacity: Optional[float] = None
        self._hover_zone_original_edges: bool = False
        self._hover_zone_original_visibility: Optional[bool] = None
        self._hover_zone_contour_original_color: Optional[tuple] = None
        self._grid_meshes: list = []
        self._selected_cells: set = set()
        self._grid_cells: dict = {}
        self._grid_origin = (0, 0, 0)
        self._selected_height = 0
        self._selection_anchor = None  # (min_x, min_y)
        self._contour_zone_overlay_enabled = False
        self._contour_zone_mode = None
        self._contour_zone_id = None
        self._contour_ignore_zone_id = None
        self._contour_ready = False
        self._grid_plane_z_override = None

        # Кэш сетки — повторное использование сетки без пересоздания
        self._grid_ready = False
        self._grid_cached_cell_size = 0
        self._grid_cached_z_size = 0

        # Атрибуты контура/сетки — инициализация для стабильности
        self._grid_nodes: list = []
        self._grid_surface_meshes: list = []
        self._grid_surface_nodes: list[tuple[float, float]] = []
        self._contour_points: list = []
        self._final_contour_points: list = []
        self._contour_points_actor = None
        self._contour_lines_actor = None
        self._contour_drag_active = False
        self._contour_drag_point_index: Optional[int] = None
        self._contour_drag_moved = False
        self._selection_start_cell = None
        self._volume_locked_from_contour = False
        self._last_volume_start_height = 0.0

        # Перетаскивание
        self._dragging = False
        self._drag_start_y = 0
        self._cam_rotate_active = False
        self._cam_pan_active = False
        self._cam_last_pos: Optional[Tuple[float, float]] = None
        self._last_safe_render_ts = 0.0
        self._custom_click_handler: Optional[Callable[[float, float, float], bool]] = None
        self._hover_handler: Optional[Callable[[float, float, float], None]] = None
        self._hover_screen_handler: Optional[Callable[[float, float], None]] = None
        self._origin_marker = None
        self._origin_preview_marker = None
        self._quadrant_actors: list = []
        self._quadrant_label_actors: list = []
        self._axes_actors: list = []
        self._last_hover_point: Optional[Tuple[float, float, float]] = None
        self._corner_points: list[tuple[float, float, float]] = []
        self._measure_ref_point: tuple[float, float, float] = (0.0, 0.0, 0.0)
        self._measure_direction: tuple[float, float] = (1.0, 1.0)
        self._ignore_next_plotter_click = False

        # Размерные линии
        self.init_dimension_lines()
        self.init_rack_placement()

        # Менеджер сценариев взаимодействия
        self._interaction_manager = InteractionManager(self)
        self._interaction_manager.set_camera_scenario(CameraScenario())
        self._interaction_manager.push(FacilityBrowseScenario())
        self._legacy_contour_binding = LegacyContourBindingComponent(self)
        self._legacy_contour_binding.install()
        self.contour_facade = ModelViewContourFacade(self)

        self.setup_ui()
        theme_bus.theme_changed.connect(self.set_theme)

    # -- Интерфейс -------------------------------------------------------------------

    def setup_ui(self):
        """Настройка интерфейса."""
        self._main_container = VContainer(
            margin=self._content_margin,
            content_fit=self._content_fit,
            parent=self,
        )

    # -- простые сеттеры / свойства ------------------------------------------

    def show_error(self, message):
        """Показать сообщение об ошибке."""
        self.error_occurred.emit(message)

    def update_scene(self) -> None:
        """Перерисовать 3D-сцену."""
        if self._plotter:
            self._plotter.update()

    def set_ignore_next_click(self, ignore: bool) -> None:
        """Пропустить следующий клик по plotter."""
        self._ignore_next_plotter_click = bool(ignore)

    def is_camera_locked(self) -> bool:
        """Заблокирована ли камера."""
        return bool(self._camera_locked)

    def is_scenario_active(self, name: str) -> bool:
        """Проверить, активен ли сценарий взаимодействия по имени."""
        mgr = getattr(self, "_interaction_manager", None)
        if mgr is not None:
            return mgr.is_active(name)
        return False

    def pop_interaction_scenario(self, name: str) -> None:
        """Снять сценарий взаимодействия по имени."""
        if self._interaction_manager is not None:
            self._interaction_manager.pop_by_name(name)

    def set_theme(self, theme: str) -> None:
        theme = (theme or "").strip().lower()
        if theme not in ("dark", "light"):
            return
        if self._theme == theme:
            return
        self._theme = theme
        if hasattr(self, "_apply_grid_theme"):
            try:
                self._apply_grid_theme()
            except Exception as e:
                log_exception(__name__, "set_theme", e)

    def set_zone_pick_enabled(self, enabled: bool) -> None:
        """Разрешить выбор существующих зон по клику."""
        self._zone_pick_enabled = bool(enabled)
        if not enabled:
            self.clear_selected_zone_highlight()
            self._unhighlight_hover_zone()

    def set_current_zone_color(self, color: str) -> None:
        """Установить цвет предпросмотра зоны (#RRGGBBAA)."""
        self._current_zone_color = color

    @property
    def room_bounds(self):
        return self._room_bounds

    @property
    def zone_selection_mode(self) -> bool:
        """Возвращает режим выбора зоны в 3D сцене."""
        return self._zone_selection_mode
