"""
Element selection mixin for GLWidget - handles element selection and hit detection
"""

from typing import Any, TYPE_CHECKING, Optional, Set

if TYPE_CHECKING:
    from PyQt6.QtWidgets import QMainWindow

from pyPhotoAlbum.models import BaseLayoutElement


class ElementSelectionMixin:
    # Type hints for expected attributes from mixing class
    _page_renderers: list

    def window(self) -> "QMainWindow":  # type: ignore[empty-body]
        """Expected from QWidget"""
        ...

    """
    Mixin providing element selection and hit detection functionality.

    This mixin manages which elements are selected and provides methods to
    detect which element or resize handle is at a given screen position.
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Selection state - multi-select support
        self.selected_elements: Set[BaseLayoutElement] = set()

    @property
    def selected_element(self) -> Optional[BaseLayoutElement]:
        """
        For backward compatibility - returns first selected element or None.

        Returns:
            BaseLayoutElement or None: The first selected element, or None if no selection
        """
        return next(iter(self.selected_elements)) if self.selected_elements else None

    @selected_element.setter
    def selected_element(self, value: Optional[BaseLayoutElement]):
        """
        For backward compatibility - sets single element selection.

        Args:
            value: Element to select, or None to clear selection
        """
        if value is None:
            self.selected_elements.clear()
        else:
            self.selected_elements = {value}

    def _get_element_at(self, x: float, y: float) -> Optional[BaseLayoutElement]:
        """
        Get the element at the given screen position across all pages.

        Args:
            x: Screen X coordinate
            y: Screen Y coordinate

        Returns:
            BaseLayoutElement or None: The topmost element at the position, or None
        """
        if not hasattr(self, "_page_renderers") or not self._page_renderers:
            return None

        # Check each page from top to bottom (reverse z-order)
        for renderer, page in reversed(self._page_renderers):
            # Convert screen coordinates to page-local coordinates
            # Do this for all pages, not just those where the click is within bounds
            # This allows selecting elements that have moved off the page
            page_x, page_y = renderer.screen_to_page(x, y)

            # Check elements in this page (highest in list = on top, so check in reverse)
            for element in reversed(page.layout.elements):
                # Get element bounds
                ex, ey = element.position
                ew, eh = element.size

                # Simple bounds check (no rotation transformation needed - images are already rotated)
                if ex <= page_x <= ex + ew and ey <= page_y <= ey + eh:
                    # Store the renderer with the element for later use
                    element._page_renderer = renderer  # type: ignore[attr-defined]
                    element._parent_page = page  # type: ignore[attr-defined]
                    return element  # type: ignore[no-any-return]

        return None

    def _get_resize_handle_at(self, x: float, y: float) -> Optional[str]:
        """
        Get the resize handle at the given screen position.

        Only checks if there is a single selected element.

        Args:
            x: Screen X coordinate
            y: Screen Y coordinate

        Returns:
            str or None: Handle name ('nw', 'ne', 'sw', 'se') or None
        """
        if not self.selected_element:
            return None

        main_window = self.window()
        if not hasattr(main_window, "project") or not main_window.project or not main_window.project.pages:
            return None

        # Get the PageRenderer for this element (stored when element was selected)
        if not hasattr(self.selected_element, "_page_renderer"):
            return None

        renderer: Any = self.selected_element._page_renderer  # type: ignore[attr-defined]

        # Get element position and size in page-local coordinates
        elem_x, elem_y = self.selected_element.position
        elem_w, elem_h = self.selected_element.size
        handle_size = 8

        # Convert to screen coordinates using PageRenderer
        ex, ey = renderer.page_to_screen(elem_x, elem_y)
        ew = elem_w * renderer.zoom
        eh = elem_h * renderer.zoom

        # Check handles (no rotation transformation needed - images are already rotated)
        handles = {
            "nw": (ex - handle_size / 2, ey - handle_size / 2),
            "ne": (ex + ew - handle_size / 2, ey - handle_size / 2),
            "sw": (ex - handle_size / 2, ey + eh - handle_size / 2),
            "se": (ex + ew - handle_size / 2, ey + eh - handle_size / 2),
        }

        for name, (hx, hy) in handles.items():
            if hx <= x <= hx + handle_size and hy <= y <= hy + handle_size:
                return name

        return None
