# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_presentation.py
"""Фасад видимости сцены для применения декларативных спецификаций представления."""

from __future__ import annotations

from typing import TYPE_CHECKING

from error_logger import log_exception

if TYPE_CHECKING:
    from gui.components.model_view_widget import ModelViewWidget


class ScenePresentationMixin:
    """Применение спецификаций видимости сцены через публичный API ModelView."""

    @staticmethod
    def _safe_call(fn, *args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except Exception as exc:
            log_exception(__name__, "ScenePresentationMixin._safe_call", exc)
            return None

    @staticmethod
    def _rack_filter_value(spec) -> str:
        rack_filter = getattr(spec, "rack_filter", "")
        return str(getattr(rack_filter, "value", rack_filter) or "")

    def apply_scene_spec(
        self: "ModelViewWidget",
        spec,
        *,
        start_select_rack: bool = False,
        select_rack_id: str | None = None,
    ) -> None:
        """Применить ``SceneVisibilitySpec``, созданный политикой представления."""
        if self is None or spec is None:
            return

        zone_contour_id = getattr(spec, "zone_contour_id", None)
        if zone_contour_id:
            self._safe_call(self.show_zone_contour, zone_contour_id)
        elif bool(getattr(spec, "zone_solids_visible", False)):
            self._safe_call(self.show_all_zones)

        if bool(getattr(spec, "use_facility_contour_mode", False)) and hasattr(
            self, "set_zone_facility_contour_mode"
        ):
            zone_ids = [str(zid) for zid in getattr(self, "_zones", {}).keys()]
            for zid in zone_ids:
                has_racks = self.has_racks_in_zone(zid) if hasattr(self, "has_racks_in_zone") else False
                self._safe_call(self.set_zone_facility_contour_mode, zid, has_racks)

        if bool(getattr(spec, "hide_empty_zones", False)) and hasattr(self, "has_racks_in_zone"):
            zone_ids = [str(zid) for zid in getattr(self, "_zones", {}).keys()]
            for zid in zone_ids:
                if not self.has_racks_in_zone(zid):
                    self._safe_call(self.set_zone_visibility, zid, False)
                else:
                    self._safe_call(self.set_zone_facility_contour_mode, zid, False)
                    self._safe_call(self.set_zone_visibility, zid, False)

        self._safe_call(self.set_zone_pick_enabled, bool(getattr(spec, "zone_pick_enabled", True)))

        rack_filter = self._rack_filter_value(spec)
        rack_zone_id = getattr(spec, "rack_zone_id", None)
        rack_id = getattr(spec, "rack_id", None)
        if rack_filter == "single_rack":
            isolated = False
            if hasattr(self, "show_only_rack") and rack_id:
                isolated = bool(self._safe_call(self.show_only_rack, rack_id, rack_zone_id))
            if not isolated and rack_zone_id:
                self._safe_call(self.show_only_zone_racks, rack_zone_id)
        elif rack_filter == "zone_only":
            self._safe_call(self.show_only_zone_racks, rack_zone_id)
        else:
            self._safe_call(self.show_all_racks)

        rack_select_mode = bool(getattr(spec, "rack_select_mode", False))
        if rack_select_mode and start_select_rack and rack_zone_id:
            self._safe_call(self.start_select_rack_mode, rack_zone_id)
        elif not rack_select_mode:
            self._safe_call(self.stop_select_rack_mode)

        if select_rack_id and hasattr(self, "select_rack_by_id"):
            self._safe_call(self.select_rack_by_id, select_rack_id, rack_zone_id)

        if bool(getattr(spec, "clear_hover", False)):
            mgr = getattr(self, "_interaction_manager", None)
            if mgr is not None:
                self._safe_call(mgr.reset)

        self._safe_call(self.set_top_view_navigation, bool(getattr(spec, "top_view_navigation", False)))
        if hasattr(self, "update_scene"):
            self._safe_call(self.update_scene)


# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
# 1) Задача модуля:
#    Применение наборов параметров отображения сцены и безопасный вызов методов представления.
#
# 2) Последовательность действий и вызовов:
#    A. Класс ScenePresentationMixin: точки входа
#       Публичные методы сценария:
#       - ScenePresentationMixin.apply_scene_spec(...)
#
#    B. ScenePresentationMixin: основной сценарий:
#       ScenePresentationMixin.apply_scene_spec(...)
#         Назначение: Применить ``SceneVisibilitySpec``, созданный политикой представления.
#         Последовательность внутренних вызовов:
#         -> ScenePresentationMixin._safe_call(...)
#         -> ScenePresentationMixin._rack_filter_value(...)
#
#    C. ScenePresentationMixin: вспомогательные расчёты:
#       ScenePresentationMixin._safe_call(...)
#         Назначение: выполняет шаг "safe call" в рамках текущего сценария модуля.
#       ScenePresentationMixin._rack_filter_value(...)
#         Назначение: выполняет шаг "rack filter value" в рамках текущего сценария модуля.
#
# 3) Важные ограничения и инварианты:
#    - Модуль выполняется в составе ModelViewWidget и использует согласованные поля состояния self._... .
#    - Межмодульная связность: только через фасад model_view; прямые обращения между zone, rack, shelf, cell запрещены.
#    - Очистка состояния должна быть идемпотентной: повторный вызов не меняет корректное состояние в ошибочное.
#
# 4) Правило сопровождения:
#    - Любое изменение сценария должно сопровождаться обновлением этого блока с сохранением фактического порядка вызовов.
#    - При добавлении метода указывать его место в цепочке сценария (запуск, основной шаг, завершение, вспомогательная логика).
