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

# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_grid_contour_overlay.py
# Режим наложения и обнаружение / валидация зон

from __future__ import annotations

from typing import TYPE_CHECKING

from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QMessageBox

from error_logger import log_exception

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

try:
    import pyvista as pv
except ImportError:  # pragma: no cover
    pv = None

class ContourOverlayComponentPart2:
    def _validate_contour_closing_segment(self: "ModelViewWidget") -> bool:
        if len(self._contour_points) < 4:
            return True
        step = max(1.0, float(getattr(self, "_current_zone_size", 1.0)))
        eps = max(1e-6, step * 0.25)
        is_overlay = bool(getattr(self, "_contour_zone_mode", "") == "overlay")
        a = self._contour_points[-1]
        b = self._contour_points[0]
        for polygon in self._get_contour_blocking_polygons():
            if is_overlay:
                if self._segment_crosses_polygon_strict(a, b, polygon, eps=eps):
                    return False
            elif self._segment_intersects_polygon_any(a, b, polygon, eps=eps):
                return False
        return True

    def _set_overlay_mode(self: "ModelViewWidget", zone_id: str) -> None:
        """Активировать режим наложения поверх существующей зоны."""
        self._restore_overlay_parent_visual()
        self._contour_zone_mode = "overlay"
        self._contour_zone_id = zone_id
        top_z = self._get_zone_top_height(zone_id)
        self._grid_plane_z_override = top_z
        self._last_volume_start_height = top_z
        actor = self._zones.get(zone_id)
        if actor is not None:
            try:
                self._overlay_parent_zone_id = zone_id
                self._overlay_parent_original_visibility = bool(actor.GetVisibility())
                self._overlay_parent_original_opacity = float(actor.GetProperty().GetOpacity())
                actor.SetVisibility(1)
                actor.GetProperty().SetOpacity(1.0)
            except Exception as e:
                log_exception(__name__, "_set_overlay_mode", e)
                self._overlay_parent_zone_id = None
                self._overlay_parent_original_visibility = None
                self._overlay_parent_original_opacity = None
        try:
            self._hide_grid_actors()
        except Exception as e:
            log_exception(__name__, "_set_overlay_mode", e)
        self._build_surface_grid_on_zone(zone_id)

    def _build_surface_grid_on_zone(self: "ModelViewWidget", zone_id: str) -> None:
        """Построить проекцию базовой сетки на верхнюю грань родительской зоны."""
        self._clear_surface_grid()

        if not self._plotter or not self._grid_cells:
            return

        polygon = self._zone_polygons.get(zone_id)
        if not polygon:
            return

        surface_z = float(self._grid_plane_z_override or 0.0)
        step = max(1.0, float(self._current_zone_size))
        eps = max(1e-6, step * 0.25)

        line_points: list[list[float]] = []
        line_cells: list[int] = []
        surface_nodes: set[tuple[float, float]] = set()

        def _add_seg(p1: tuple, p2: tuple) -> None:
            i1 = len(line_points)
            line_points.append([p1[0], p1[1], p1[2]])
            i2 = len(line_points)
            line_points.append([p2[0], p2[1], p2[2]])
            line_cells.extend([2, i1, i2])

        for _cid, (mn_x, mx_x, mn_y, mx_y) in self._grid_cells.items():
            cx = (mn_x + mx_x) * 0.5
            cy = (mn_y + mx_y) * 0.5
            inside, boundary = self._classify_point_in_polygon(cx, cy, polygon, eps=eps)
            if not (inside or boundary):
                continue
            p00 = (mn_x, mn_y, surface_z)
            p10 = (mx_x, mn_y, surface_z)
            p11 = (mx_x, mx_y, surface_z)
            p01 = (mn_x, mx_y, surface_z)
            surface_nodes.add((float(mn_x), float(mn_y)))
            surface_nodes.add((float(mx_x), float(mn_y)))
            surface_nodes.add((float(mx_x), float(mx_y)))
            surface_nodes.add((float(mn_x), float(mx_y)))
            _add_seg(p00, p10)
            _add_seg(p10, p11)
            _add_seg(p11, p01)
            _add_seg(p01, p00)

        if not line_points:
            return

        try:
            grid_lines = pv.PolyData(line_points)
            grid_lines.lines = line_cells
            color = self._get_grid_color()
            rgb = (color[0] / 255.0, color[1] / 255.0, color[2] / 255.0)
            actor = self._plotter.add_mesh(
                grid_lines, color=rgb, line_width=1,
            )
            self._grid_surface_meshes.append(actor)
            self._grid_surface_nodes = list(surface_nodes)
        except Exception as e:
            log_exception(__name__, "_build_surface_grid_on_zone", e)

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

    def _resolve_overlay_first_point(
        self: "ModelViewWidget", nx: float, ny: float,
    ) -> bool:
        """Определить режим overlay/ground при первой точке контура.

        Возвращает True если точку можно добавить, False — отклонить.
        """
        ranked_zones = self._classify_point_in_zones_ranked(nx, ny)
        if not ranked_zones:
            return True
        zone_id, kind, _ = ranked_zones[0]

        def _apply_overlay_with_height_check(candidate_zone_id: str) -> bool:
            self._set_overlay_mode(candidate_zone_id)
            min_height = max(1.0, float(getattr(self, "_current_zone_z_size", self._current_zone_size)))
            if self._get_max_height_for_start_z(self._get_contour_plane_z()) < min_height:
                self._contour_zone_mode = None
                self._contour_zone_id = None
                self._grid_plane_z_override = None
                self._clear_surface_grid()
                return False
            return True

        # Фильтруем зоны с видом «inside» или «boundary»
        valid_candidates = [
            (zid, k, tz) for zid, k, tz in ranked_zones
            if k in ("inside", "boundary")
        ]

        if kind == "inside" or kind == "boundary":
            # Отложенный запрос подтверждения: вынос из VTK-callback через QTimer
            def _deferred_overlay_question():
                reply = QMessageBox.question(
                    self,
                    "Расположение точки",
                    "Расположение вашей точки планируется\n"
                    "поверх существующей зоны?",
                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                    QMessageBox.StandardButton.No,
                )
                if reply == QMessageBox.StandardButton.Yes:
                    if len(valid_candidates) >= 2:
                        self._start_zone_selection_for_overlay(
                            nx, ny, valid_candidates, _apply_overlay_with_height_check
                        )
                        return
                    if _apply_overlay_with_height_check(zone_id):
                        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()
                    return
                # Пользователь отказался — всегда остаёмся в ground-режиме.
                self._set_ground_mode()
                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()

            QTimer.singleShot(0, _deferred_overlay_question)
            return False

        return True
