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

# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_grid_sel_handlers.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 ContourSelectionComponent:
    def _is_contour_ready_for_volume(self: "ModelViewWidget") -> bool:
        polygon = self._extract_orthogonal_polygon()
        if polygon is None:
            return False
        if not self._selected_cells:
            return False
        try:
            if not self._validate_contour_closing_segment():
                return False
        except Exception as e:
            log_exception(__name__, "_is_contour_ready_for_volume", e)
            return False
        return True

    def _emit_contour_ready_state(self: "ModelViewWidget") -> None:
        ready = bool(self.is_scenario_active("contour_edit") and self._is_contour_ready_for_volume())
        prev = bool(getattr(self, "_contour_ready", False))
        self._contour_ready = ready
        if prev != ready:
            try:
                self.contour_ready_changed.emit(ready)
            except Exception as e:
                log_exception(__name__, "_emit_contour_ready_state", e)

    def _handle_grid_click(
        self: "ModelViewWidget",
        x: float, y: float, z: float,
        action: str = "add",
    ):
        # --- Режим контура ---
        if self.is_scenario_active("contour_edit"):
            if action == "add":
                node = self._nearest_grid_node(x, y)
                if node is None:
                    return
                if bool(getattr(self, "_contour_aux_hidden", False)):
                    if hasattr(self, "set_contour_auxiliary_visibility"):
                        self.set_contour_auxiliary_visibility(True)
                if node in self._contour_points:
                    return

                # -- Валидация наложения --
                if self._contour_zone_overlay_enabled:
                    if self._contour_zone_mode is None:
                        # Первая точка — определяем режим overlay/ground.
                        if not self._resolve_overlay_first_point(node[0], node[1]):
                            return
                    else:
                        # Последующие точки — проверяем допустимость.
                        if not self._validate_overlay_point(node[0], node[1]):
                            return

                if not self._validate_contour_point_against_existing_volumes(node[0], node[1]):
                    return

                if hasattr(self, "_append_contour_point"):
                    if not self._append_contour_point(node):
                        return
                else:
                    self._contour_points.append(node)
                # Обновить «последнюю точку» для размерных линий
                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
            if action == "remove":
                removed = self._remove_nearest_contour_point(x, y)
                if removed:
                    # Пересчитать последнюю точку после удаления
                    if getattr(self, "_dim_enabled", False):
                        if self._contour_points:
                            self.set_dim_last_point(self._contour_points[-1])
                        else:
                            self.set_dim_last_point(None)
                    self._update_contour_visualization()
                    self._update_selected_cells_from_contour()
                else:
                    # ПКМ вне узла: скрыть вспомогательные акторы для чистого обзора.
                    if hasattr(self, "set_contour_auxiliary_visibility"):
                        self.set_contour_auxiliary_visibility(False)
                self._emit_contour_ready_state()
                return

        # --- Обычный режим сетки ---
        found = self._find_cell_by_point(x, y, prefer_selected=(action == "remove"))
        if not found:
            return
        cell_id, (mn_x, mx_x, mn_y, mx_y) = found

        if action == "add":
            self._handle_grid_add(cell_id, mn_x, mx_x, mn_y, mx_y)
        elif action == "remove":
            self._handle_grid_remove(cell_id, mn_x, mx_x, mn_y, mx_y)
        else:
            return

        self._selection_anchor = self._resolve_selection_anchor()
        self._update_selection_visualization()

    def _handle_grid_add(
        self: "ModelViewWidget",
        cell_id: int,
        mn_x: float, mx_x: float,
        mn_y: float, mx_y: float,
    ):
        """ЛКМ: расширить выделение прямоугольником от стартовой ячейки."""
        eps = 1e-6
        if not self._selected_cells:
            self._selection_start_cell = (mn_x, mx_x, mn_y, mx_y)
            self._selected_cells.add(cell_id)
            return

        start_cell = self._selection_start_cell
        if start_cell is None:
            start_cell = (mn_x, mx_x, mn_y, mx_y)
            self._selection_start_cell = start_cell
        s_mn_x, s_mx_x, s_mn_y, s_mx_y = start_cell
        add_min_x = min(s_mn_x, mn_x)
        add_max_x = max(s_mx_x, mx_x)
        add_min_y = min(s_mn_y, mn_y)
        add_max_y = max(s_mx_y, mx_y)

        to_add = set()
        for candidate_id, (c_mn_x, c_mx_x, c_mn_y, c_mx_y) in self._grid_cells.items():
            if (
                c_mn_x >= add_min_x - eps
                and c_mx_x <= add_max_x + eps
                and c_mn_y >= add_min_y - eps
                and c_mx_y <= add_max_y + eps
            ):
                if candidate_id not in self._selected_cells:
                    to_add.add(candidate_id)
        if not to_add:
            return
        self._selected_cells.update(to_add)

    def _handle_grid_remove(
        self: "ModelViewWidget",
        cell_id: int,
        mn_x: float, mx_x: float,
        mn_y: float, mx_y: float,
    ):
        """ПКМ: усечение выделения."""
        if not self._selected_cells:
            return

        selected_cells = [
            self._grid_cells[cid]
            for cid in self._selected_cells
            if cid in self._grid_cells
        ]
        if not selected_cells:
            return

        sel_min_x = min(c[0] for c in selected_cells)
        sel_max_x = max(c[1] for c in selected_cells)
        sel_min_y = min(c[2] for c in selected_cells)
        sel_max_y = max(c[3] for c in selected_cells)
        eps = 1e-6

        # ПКМ за пределами текущего объёма — игнорируем.
        if (
            mx_x < sel_min_x - eps or mn_x > sel_max_x + eps
            or mx_y < sel_min_y - eps or mn_y > sel_max_y + eps
        ):
            return

        start_cell = self._selection_start_cell
        if start_cell is None:
            start_cell = (mn_x, mx_x, mn_y, mx_y)
            self._selection_start_cell = start_cell

        # ПКМ по стартовой ячейке — сброс всего выделения.
        s_mn_x, s_mx_x, s_mn_y, s_mx_y = start_cell
        if (
            abs(mn_x - s_mn_x) <= eps and abs(mx_x - s_mx_x) <= eps
            and abs(mn_y - s_mn_y) <= eps and abs(mx_y - s_mx_y) <= eps
        ):
            self._selected_cells = set()
            self._selection_anchor = None
            self._update_selection_visualization()
            return

        keep_min_x = min(s_mn_x, mn_x)
        keep_max_x = max(s_mx_x, mx_x)
        keep_min_y = min(s_mn_y, mn_y)
        keep_max_y = max(s_mx_y, mx_y)

        trimmed_cells = set()
        for selected_id in self._selected_cells:
            c_mn_x, c_mx_x, c_mn_y, c_mx_y = self._grid_cells[selected_id]
            if (
                c_mn_x >= keep_min_x - eps
                and c_mx_x <= keep_max_x + eps
                and c_mn_y >= keep_min_y - eps
                and c_mx_y <= keep_max_y + eps
            ):
                trimmed_cells.add(selected_id)
        if not trimmed_cells:
            return
        self._selected_cells = trimmed_cells
