"""
Element manipulation mixin for GLWidget - handles element transformations
"""

from typing import TYPE_CHECKING, Any, Optional, Tuple

if TYPE_CHECKING:
    from pyPhotoAlbum.models import BaseLayoutElement
    from PyQt6.QtWidgets import QMainWindow


class ElementManipulationMixin:
    # Type hints for expected attributes from mixing class
    selected_element: Optional["BaseLayoutElement"]
    drag_start_pos: Optional[Tuple[float, float]]
    drag_start_element_pos: Optional[Tuple[float, float]]

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

    def __init__(self, *args, **kwargs):
        """
        Initialize element manipulation mixin.

        This mixin provides element transformation functionality including
        resizing, rotating, moving elements, snapping support and cross-page
        element transfers.
        """
        super().__init__(*args, **kwargs)

        # Resize state
        self.resize_handle: Optional[str] = None  # 'nw', 'ne', 'sw', 'se'
        self.resize_start_pos: Optional[Tuple[float, float]] = None
        self.resize_start_size: Optional[Tuple[float, float]] = None

        # Rotation state
        self.rotation_mode: bool = False  # Toggle between move/resize and rotation modes
        self.rotation_start_angle: Optional[float] = None
        self.rotation_snap_angle: int = 15  # Default snap angle in degrees

        # Snap state tracking
        self.snap_state = {"is_snapped": False, "last_position": None, "last_size": None}

    def _resize_element(self, dx: float, dy: float):
        """
        Resize the element based on the resize handle.

        Args:
            dx: Delta X in page-local coordinates
            dy: Delta Y in page-local coordinates
        """
        if not self.selected_element or not self.resize_handle:
            return

        if not self.resize_start_pos or not self.resize_start_size:
            return

        # Get the snapping system from the element's parent page
        main_window = self.window()
        if not hasattr(self.selected_element, "_parent_page"):
            self._resize_element_no_snap(dx, dy)
            return

        parent_page = self.selected_element._parent_page  # type: ignore[attr-defined]
        snap_sys = parent_page.layout.snapping_system

        # Get page size
        page_size = parent_page.layout.size
        dpi = main_window.project.working_dpi  # type: ignore[attr-defined]

        # Apply snapping to resize
        from pyPhotoAlbum.snapping import SnapResizeParams

        params = SnapResizeParams(
            position=self.resize_start_pos,
            size=self.resize_start_size,
            dx=dx,
            dy=dy,
            resize_handle=self.resize_handle,
            page_size=page_size,
            dpi=dpi,
            project=main_window.project,  # type: ignore[attr-defined]
        )
        new_pos, new_size = snap_sys.snap_resize(params)

        # Apply the snapped values
        self.selected_element.position = new_pos
        self.selected_element.size = new_size

        # Ensure minimum size
        min_size = 20
        w, h = self.selected_element.size
        if w < min_size or h < min_size:
            w = max(w, min_size)
            h = max(h, min_size)
            self.selected_element.size = (w, h)

    def _resize_element_no_snap(self, dx: float, dy: float):
        """
        Resize element without snapping.

        Args:
            dx: Delta X in page-local coordinates
            dy: Delta Y in page-local coordinates
        """
        if not self.resize_start_pos or not self.resize_start_size:
            return
        if self.selected_element is None:
            return

        start_x, start_y = self.resize_start_pos
        start_w, start_h = self.resize_start_size

        if self.resize_handle == "nw":
            self.selected_element.position = (start_x + dx, start_y + dy)
            self.selected_element.size = (start_w - dx, start_h - dy)
        elif self.resize_handle == "ne":
            self.selected_element.position = (start_x, start_y + dy)
            self.selected_element.size = (start_w + dx, start_h - dy)
        elif self.resize_handle == "sw":
            self.selected_element.position = (start_x + dx, start_y)
            self.selected_element.size = (start_w - dx, start_h + dy)
        elif self.resize_handle == "se":
            self.selected_element.size = (start_w + dx, start_h + dy)

        # Ensure minimum size
        min_size = 20
        w, h = self.selected_element.size
        if w < min_size:
            self.selected_element.size = (min_size, h)
        if h < min_size:
            w, _ = self.selected_element.size
            self.selected_element.size = (w, min_size)

    def _transfer_element_to_page(
        self, element, source_page, target_page, mouse_x: float, mouse_y: float, target_renderer
    ):
        """
        Transfer an element from one page to another during drag operation.

        Args:
            element: The element to transfer
            source_page: Source page object
            target_page: Target page object
            mouse_x: Current mouse X position in screen coordinates
            mouse_y: Current mouse Y position in screen coordinates
            target_renderer: PageRenderer for the target page
        """
        # Convert mouse position to target page coordinates
        new_page_x, new_page_y = target_renderer.screen_to_page(mouse_x, mouse_y)

        # Get element size
        elem_w, elem_h = element.size

        # Center the element on the mouse position
        new_x = new_page_x - elem_w / 2
        new_y = new_page_y - elem_h / 2

        # Remove element from source page
        if element in source_page.layout.elements:
            source_page.layout.elements.remove(element)
            print(f"Removed element from page {source_page.page_number}")

        # Update element position to new page coordinates
        element.position = (new_x, new_y)

        # Add element to target page
        target_page.layout.add_element(element)

        # Update element's parent page reference
        element._parent_page = target_page
        element._page_renderer = target_renderer

        # Update drag start position and element position for continued dragging
        self.drag_start_pos = (mouse_x, mouse_y)
        self.drag_start_element_pos = element.position

        print(f"Transferred element to page {target_page.page_number} at ({new_x:.1f}, {new_y:.1f})")
