# -*- coding: utf-8 -*-
# gui/components/model_view/contour/contour_overlay_selection_component.py

# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_grid_contour_selection.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 ContourOverlaySelectionComponent:
    def _start_zone_selection_for_overlay(
        self: "ModelViewWidget",
        nx: float,
        ny: float,
        candidates: list,
        apply_fn,
    ) -> None:
        """Запустить режим выбора зоны для overlay при перекрытии 2+ зон.

        Пользователь наводит курсор на объём зоны — она подсвечивается.
        ЛКМ фиксирует выбор.
        """
        candidate_ids = {zid for zid, _, _ in candidates}
        self._overlay_select_candidates = candidate_ids
        self._overlay_select_nx = nx
        self._overlay_select_ny = ny
        self._overlay_select_apply_fn = apply_fn
        self._overlay_select_highlighted = None
        # Сохраняем исходные прозрачности зон
        self._overlay_select_original_opacities = {}
        self._overlay_select_original_visibility = {}
        for zid in candidate_ids:
            actor = self._zones.get(zid)
            if actor is not None:
                try:
                    self._overlay_select_original_opacities[zid] = actor.GetProperty().GetOpacity()
                except Exception as e:
                    log_exception(__name__, "_start_zone_selection_for_overlay", e)
                    self._overlay_select_original_opacities[zid] = 1.0
                try:
                    self._overlay_select_original_visibility[zid] = bool(actor.GetVisibility())
                except Exception as e:
                    log_exception(__name__, "_start_zone_selection_for_overlay", e)
                    self._overlay_select_original_visibility[zid] = True

        # Устанавливаем обработчики hover + click через сценарий взаимодействия

        def _resolve_world_xy(wx: float | None, wy: float | None) -> tuple[float | None, float | None]:
            try:
                from PySide6.QtGui import QCursor
                global_pos = QCursor.pos()
                local_pos = self._plotter.mapFromGlobal(global_pos)
                dpr_v = self._plotter.devicePixelRatio()
                rw_v = self._plotter.ren_win
                if rw_v:
                    _, h_v = rw_v.GetSize()
                    s_x = int(round(local_pos.x() * dpr_v))
                    s_y = h_v - int(round(local_pos.y() * dpr_v)) - 1
                    p_z = self._get_contour_plane_z()
                    world = self.screen_to_world_on_plane(s_x, s_y, p_z)
                    if world is not None:
                        return float(world[0]), float(world[1])
            except Exception as e:
                log_exception(__name__, "_resolve_world_xy", e)
            if wx is not None and wy is not None:
                return float(wx), float(wy)
            return None, None

        def _hover(x: float, y: float, z: float) -> None:
            x, y = _resolve_world_xy(x, y)
            if x is None or y is None:
                return
            best_zone = None
            best_top_z = None
            for zid in candidate_ids:
                polygon = self._zone_polygons.get(zid)
                if not polygon or len(polygon) < 3:
                    continue
                inside, boundary = self._classify_point_in_polygon(float(x), float(y), polygon)
                if not (inside or boundary):
                    continue
                top_z = self._get_zone_top_height(zid)
                if best_zone is None or float(top_z) > float(best_top_z):
                    best_zone = zid
                    best_top_z = top_z
            if best_zone == self._overlay_select_highlighted:
                return
            # Снимаем подсветку с предыдущей
            if self._overlay_select_highlighted is not None:
                prev_actor = self._zones.get(self._overlay_select_highlighted)
                orig_op = self._overlay_select_original_opacities.get(
                    self._overlay_select_highlighted, 0.3
                )
                orig_vis = self._overlay_select_original_visibility.get(
                    self._overlay_select_highlighted, True
                )
                if prev_actor is not None:
                    try:
                        prev_actor.SetVisibility(1 if orig_vis else 0)
                        prev_actor.GetProperty().SetOpacity(orig_op)
                    except Exception as e:
                        log_exception(__name__, "_hover", e)
            # Подсвечиваем новую
            self._overlay_select_highlighted = best_zone
            if best_zone is not None:
                actor = self._zones.get(best_zone)
                if actor is not None:
                    try:
                        actor.SetVisibility(1)
                        actor.GetProperty().SetOpacity(1.0)
                    except Exception as e:
                        log_exception(__name__, "_hover", e)
            if self._plotter:
                try:
                    self._plotter.update()
                except Exception as e:
                    log_exception(__name__, "_hover", e)

        def _click(x: float, y: float, z: float) -> bool:
            selected_zid = self._overlay_select_highlighted
            if selected_zid is None:
                try:
                    from vtkmodules.vtkRenderingCore import vtkPropPicker
                    sx, sy = self._plotter.interactor.GetEventPosition()
                    picker = vtkPropPicker()
                    picker.Pick(sx, sy, 0, self._plotter.renderer)
                    picked_actor = picker.GetViewProp()
                    picked_zone_id = self._find_zone_by_actor(picked_actor)
                    if picked_zone_id in candidate_ids:
                        selected_zid = picked_zone_id
                except Exception as e:
                    log_exception(__name__, "_click", e)
            if selected_zid is None:
                x, y = _resolve_world_xy(x, y)
                if x is None or y is None:
                    return True
                # Попробуем найти зону по клику
                best_top_z = None
                for zid in candidate_ids:
                    polygon = self._zone_polygons.get(zid)
                    if not polygon or len(polygon) < 3:
                        continue
                    inside, boundary = self._classify_point_in_polygon(float(x), float(y), polygon)
                    if not (inside or boundary):
                        continue
                    top_z = self._get_zone_top_height(zid)
                    if selected_zid is None or float(top_z) > float(best_top_z):
                        selected_zid = zid
                        best_top_z = top_z
            if selected_zid is None:
                return True
            self._finish_zone_selection_for_overlay(selected_zid)
            return True

        mgr = getattr(self, "_interaction_manager", None)
        if mgr is not None:
            from gui.components.model_view._scenario_custom_handler import CustomHandlerScenario
            mgr.push(CustomHandlerScenario(
                name="overlay_select",
                click_handler=_click,
                hover_handler=_hover,
            ))

    def _finish_zone_selection_for_overlay(
        self: "ModelViewWidget", zone_id: str,
    ) -> None:
        """Завершить режим выбора зоны для overlay."""
        # Восстанавливаем прозрачности
        original = getattr(self, "_overlay_select_original_opacities", {})
        original_visibility = getattr(self, "_overlay_select_original_visibility", {})
        for zid, opacity in original.items():
            actor = self._zones.get(zid)
            if actor is not None:
                try:
                    actor.SetVisibility(1 if original_visibility.get(zid, True) else 0)
                    actor.GetProperty().SetOpacity(opacity)
                except Exception as e:
                    log_exception(__name__, "_finish_zone_selection_for_overlay", e)

        # Убрать сценарий взаимодействия overlay (on_resume восстановит нижележащий)
        mgr = getattr(self, "_interaction_manager", None)
        if mgr is not None:
            mgr.pop_by_name("overlay_select")

        apply_fn = getattr(self, "_overlay_select_apply_fn", None)
        nx = getattr(self, "_overlay_select_nx", 0.0)
        ny = getattr(self, "_overlay_select_ny", 0.0)

        # Очистка временных атрибутов
        for attr in (
            "_overlay_select_candidates",
            "_overlay_select_nx",
            "_overlay_select_ny",
            "_overlay_select_apply_fn",
            "_overlay_select_highlighted",
            "_overlay_select_original_opacities",
            "_overlay_select_original_visibility",
        ):
            try:
                delattr(self, attr)
            except AttributeError as e:
                log_exception(__name__, "_finish_zone_selection_for_overlay", e)

        if apply_fn is not None:
            if apply_fn(zone_id):
                # Overlay применён — добавляем исходную точку как первую точку контура
                node = self._nearest_grid_node(nx, ny)
                if node is not None and node not in self._contour_points:
                    if self._validate_contour_point_against_existing_volumes(node[0], node[1]):
                        if not self._append_contour_point(node):
                            return
                        if getattr(self, "_dim_enabled", False):
                            self.set_dim_last_point(node)
                        self._update_contour_visualization()
                        self._update_selected_cells_from_contour()
                        self._emit_contour_ready_state()

        if self._plotter:
            try:
                self._plotter.update()
            except Exception as e:
                log_exception(__name__, "_finish_zone_selection_for_overlay", e)

    def _cleanup_overlay_selection(self: "ModelViewWidget") -> None:
        """Очистить состояние overlay-выбора зоны (если было активно)."""
        if not hasattr(self, "_overlay_select_candidates"):
            return
        # Восстанавливаем прозрачности зон
        original = getattr(self, "_overlay_select_original_opacities", {})
        original_visibility = getattr(self, "_overlay_select_original_visibility", {})
        for zid, opacity in original.items():
            actor = self._zones.get(zid)
            if actor is not None:
                try:
                    actor.SetVisibility(1 if original_visibility.get(zid, True) else 0)
                    actor.GetProperty().SetOpacity(opacity)
                except Exception as e:
                    log_exception(__name__, "_cleanup_overlay_selection", e)
        # Убрать сценарий взаимодействия overlay
        mgr = getattr(self, "_interaction_manager", None)
        if mgr is not None:
            mgr.pop_by_name("overlay_select")
        # Очищаем временные атрибуты
        for attr in (
            "_overlay_select_candidates",
            "_overlay_select_nx",
            "_overlay_select_ny",
            "_overlay_select_apply_fn",
            "_overlay_select_highlighted",
            "_overlay_select_original_opacities",
            "_overlay_select_original_visibility",
        ):
            try:
                delattr(self, attr)
            except AttributeError as e:
                log_exception(__name__, "_cleanup_overlay_selection", e)

    def _validate_overlay_point(
        self: "ModelViewWidget", nx: float, ny: float,
    ) -> bool:
        """Проверить допустимость точки при уже определённом overlay-режиме.

        overlay: точка должна быть внутри или на границе родительской зоны.
        ground:  точка НЕ должна попадать внутрь или на границу любой зоны.
        """
        if self._contour_zone_mode == "overlay":
            return self._point_in_zone_or_boundary(
                nx, ny, self._contour_zone_id, plane_z=self._get_contour_plane_z()
            )

        if self._contour_zone_mode == "ground":
            ranked = self._classify_point_in_zones_ranked(
                nx, ny, plane_z=self._get_contour_plane_z()
            )
            return len(ranked) == 0

        return True
