"""
Size operations mixin for pyPhotoAlbum
"""

from pyPhotoAlbum.decorators import ribbon_action
from pyPhotoAlbum.alignment import AlignmentManager
from pyPhotoAlbum.commands import ResizeElementsCommand


class SizeOperationsMixin:
    """Mixin providing element sizing operations"""

    def _get_selected_elements_list(self):
        """Get list of selected elements for size operations"""
        return list(self.gl_widget.selected_elements) if self.gl_widget.selected_elements else []

    def _execute_resize(self, resize_func, status_msg: str):
        """
        Execute a resize operation on multiple elements.

        Args:
            resize_func: AlignmentManager method to call with elements
            status_msg: Status message format string (will receive element count)
        """
        elements = self._get_selected_elements_list()
        if not self.require_selection(min_count=2):  # type: ignore[attr-defined]
            return

        changes = resize_func(elements)
        if changes:
            cmd = ResizeElementsCommand(changes)
            self.project.history.execute(cmd)  # type: ignore[attr-defined]
            self.update_view()  # type: ignore[attr-defined]
            self.show_status(status_msg.format(len(elements)), 2000)  # type: ignore[attr-defined]

    def _execute_fit_to_page(self, fit_func, status_msg: str):
        """
        Execute a fit-to-page operation on a single element.

        Args:
            fit_func: Function that takes (element, page) and returns a change tuple
            status_msg: Status message to display on success
        """
        if not self.require_selection(min_count=1):  # type: ignore[attr-defined]
            return

        page = self.get_current_page()  # type: ignore[attr-defined]
        if not page:
            self.show_warning("No Page", "Please create a page first.")  # type: ignore[attr-defined]
            return

        element = next(iter(self.gl_widget.selected_elements))  # type: ignore[attr-defined]
        change = fit_func(element, page)

        if change:
            cmd = ResizeElementsCommand([change])
            self.project.history.execute(cmd)  # type: ignore[attr-defined]
            self.update_view()  # type: ignore[attr-defined]
            self.show_status(status_msg, 2000)  # type: ignore[attr-defined]

    @ribbon_action(
        label="Same Size",
        tooltip="Make all selected elements the same size",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=2,
    )
    def make_same_size(self):
        """Make all selected elements the same size"""
        self._execute_resize(AlignmentManager.make_same_size, "Resized {} elements to same size")

    @ribbon_action(
        label="Same Width",
        tooltip="Make all selected elements the same width",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=2,
    )
    def make_same_width(self):
        """Make all selected elements the same width"""
        self._execute_resize(AlignmentManager.make_same_width, "Resized {} elements to same width")

    @ribbon_action(
        label="Same Height",
        tooltip="Make all selected elements the same height",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=2,
    )
    def make_same_height(self):
        """Make all selected elements the same height"""
        self._execute_resize(AlignmentManager.make_same_height, "Resized {} elements to same height")

    @ribbon_action(
        label="Fit Width",
        tooltip="Fit selected element to page width",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=1,
    )
    def fit_to_width(self):
        """Fit selected element to page width"""
        self._execute_fit_to_page(
            lambda elem, page: AlignmentManager.fit_to_page_width(elem, page.layout.size[0]),
            "Fitted element to page width",
        )

    @ribbon_action(
        label="Fit Height",
        tooltip="Fit selected element to page height",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=1,
    )
    def fit_to_height(self):
        """Fit selected element to page height"""
        self._execute_fit_to_page(
            lambda elem, page: AlignmentManager.fit_to_page_height(elem, page.layout.size[1]),
            "Fitted element to page height",
        )

    @ribbon_action(
        label="Fit to Page",
        tooltip="Fit selected element to page dimensions",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=1,
    )
    def fit_to_page(self):
        """Fit selected element to page dimensions"""
        self._execute_fit_to_page(
            lambda elem, page: AlignmentManager.fit_to_page(elem, page.layout.size[0], page.layout.size[1]),
            "Fitted element to page",
        )

    @ribbon_action(
        label="Expand Image",
        tooltip="Expand selected image to fill available space until it reaches page edges or other elements",
        tab="Arrange",
        group="Size",
        requires_selection=True,
        min_selection=1,
    )
    def expand_image(self):
        """Expand selected image to fill available space"""
        if not self.require_selection(min_count=1):
            return

        page = self.get_current_page()
        if not page:
            self.show_warning("No Page", "Please create a page first.")
            return

        # Get the first selected element
        element = next(iter(self.gl_widget.selected_elements))

        # Get other elements on the same page (excluding the selected one)
        other_elements = [e for e in page.layout.elements if e is not element]

        # Use configurable min_gap (grid spacing from snapping system, default 10mm)
        min_gap = getattr(page.layout.snapping_system, "grid_spacing", 10.0)

        # Expand to bounds
        page_width, page_height = page.layout.size
        change = AlignmentManager.expand_to_bounds(element, (page_width, page_height), other_elements, min_gap)

        if change:
            cmd = ResizeElementsCommand([change])
            self.project.history.execute(cmd)
            self.update_view()
            self.show_status(f"Expanded image with {min_gap}mm gap", 2000)
