# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_zone_transition.py
"""Вспомогательные методы плавного перехода камеры для навигации на уровне зон."""

from __future__ import annotations

from typing import TYPE_CHECKING

from error_logger import log_exception

if TYPE_CHECKING:  # pragma: no cover
    from gui.components.model_view_widget import ModelViewWidget


class ZoneCameraTransitionMixin:
    """Анимация фокусировки камеры на выбранную зону в изометрическом виде."""

    def focus_on_zone_isometric(
        self: "ModelViewWidget",
        zone_id: str | None,
        *,
        duration_ms: int = 340,
    ) -> bool:
        if not self._plotter or not zone_id:
            return False
        bounds = (self._zone_data or {}).get(str(zone_id))
        if not bounds:
            return False
        cam = getattr(self._plotter, "camera", None)
        if cam is None:
            return False
        if not hasattr(self, "_animate_camera_transition"):
            return False
        try:
            min_x, max_x, min_y, max_y, min_z, max_z = [float(v) for v in bounds]
            cx = (min_x + max_x) * 0.5
            cy = (min_y + max_y) * 0.5
            sx = max(1.0, max_x - min_x)
            sy = max(1.0, max_y - min_y)
            sz = max(1.0, max_z - min_z)
            cz = min_z + (sz * 0.52)
            span = max(sx, sy, sz)

            start_pos = tuple(float(v) for v in cam.GetPosition())
            start_focal = tuple(float(v) for v in cam.GetFocalPoint())
            start_up = tuple(float(v) for v in cam.GetViewUp())

            vx, vy, vz = 1.0, -1.0, 0.75
            norm = (vx * vx + vy * vy + vz * vz) ** 0.5
            vx, vy, vz = vx / norm, vy / norm, vz / norm

            ux, uy, uz = 0.0, 0.0, 1.0
            fx, fy, fz = -vx, -vy, -vz
            rx = fy * uz - fz * uy
            ry = fz * ux - fx * uz
            rz = fx * uy - fy * ux
            r_norm = (rx * rx + ry * ry + rz * rz) ** 0.5
            if r_norm <= 1e-6:
                rx, ry, rz = 1.0, 0.0, 0.0
            else:
                rx, ry, rz = rx / r_norm, ry / r_norm, rz / r_norm

            zone_margin = max(1.01, float(getattr(self, "_zone_iso_margin_factor", 1.24)))
            zone_min_distance = max(1000.0, float(getattr(self, "_zone_iso_min_distance", 2600.0)))
            zone_side_shift = max(0.0, float(getattr(self, "_zone_iso_side_shift_factor", 0.05)))
            zone_z_lift = max(0.0, float(getattr(self, "_zone_iso_z_lift_factor", 0.08)))
            zone_distance_scale = max(1.0, float(getattr(self, "_zone_iso_distance_scale", 1.30)))
            if hasattr(self, "_compute_isometric_fit_distance"):
                distance = float(
                    self._compute_isometric_fit_distance(
                        (min_x, max_x, min_y, max_y, min_z, max_z),
                        view_dir=(vx, vy, vz),
                        up_dir=(ux, uy, uz),
                        margin_factor=zone_margin,
                        min_distance=zone_min_distance,
                    )
                )
            else:
                distance = max(zone_min_distance, span * 2.4)
            distance *= zone_distance_scale
            target_focal = (cx, cy, cz)
            target_pos = (
                cx + vx * distance + rx * (span * zone_side_shift),
                cy + vy * distance + ry * (span * zone_side_shift),
                cz + vz * distance + (sz * zone_z_lift),
            )
            self._animate_camera_transition(
                start_pos=start_pos,
                start_focal=start_focal,
                start_up=start_up,
                target_pos=target_pos,
                target_focal=target_focal,
                target_up=(ux, uy, uz),
                duration_ms=int(duration_ms),
                steps=18,
            )
            return True
        except Exception as exc:
            log_exception(__name__, "focus_on_zone_isometric", exc)
            return False


# ---------------------------------------------------------------------------
# Module workflow notes
# ---------------------------------------------------------------------------
# 1) Задача модуля:
#    Переход камеры к выбранной зоне.
#
# 2) Последовательность действий и вызовов:
#    A. Класс ZoneCameraTransitionMixin: точки входа
#       Публичные методы сценария:
#       - ZoneCameraTransitionMixin.focus_on_zone_isometric(...)
#
#    B. ZoneCameraTransitionMixin: основной сценарий:
#       ZoneCameraTransitionMixin.focus_on_zone_isometric(...)
#         Назначение: фокусирует on zone isometric в рамках текущего сценария модуля.
#
# 3) Важные ограничения и инварианты:
#    - Модуль выполняется в составе ModelViewWidget и использует согласованные поля состояния self._... .
#    - Все операции с актёрами и камерой выполняются только при валидном self._plotter с безопасной обработкой исключений.
#    - Межмодульная связность: только через фасад model_view; прямые обращения между zone, rack, shelf, cell запрещены.
#    - Очистка состояния должна быть идемпотентной: повторный вызов не меняет корректное состояние в ошибочное.
#
# 4) Правило сопровождения:
#    - Любое изменение сценария должно сопровождаться обновлением этого блока с сохранением фактического порядка вызовов.
#    - При добавлении метода указывать его место в цепочке сценария (запуск, основной шаг, завершение, вспомогательная логика).
