"""
Style operations mixin for pyPhotoAlbum

Provides ribbon actions for applying visual styles to images:
- Rounded corners
- Borders
- Drop shadows
- (Future) Decorative frames
"""

from pyPhotoAlbum.decorators import ribbon_action
from pyPhotoAlbum.models import ImageData, ImageStyle


class StyleOperationsMixin:
    """Mixin providing element styling operations"""

    def _get_selected_images(self):
        """Get list of selected ImageData elements"""
        if not self.gl_widget.selected_elements:
            return []
        return [e for e in self.gl_widget.selected_elements if isinstance(e, ImageData)]

    def _apply_style_change(self, style_updater, description: str):
        """
        Apply a style change to selected images with undo support.

        Args:
            style_updater: Function that takes an ImageStyle and modifies it
            description: Description for undo history
        """
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        # Store old styles for undo
        old_styles = [(img, img.style.copy()) for img in images]

        # Create undo command
        from pyPhotoAlbum.commands import Command

        class StyleChangeCommand(Command):
            def __init__(self, old_styles, new_style_updater, desc):
                self.old_styles = old_styles
                self.new_style_updater = new_style_updater
                self.description = desc

            def _invalidate_texture(self, img):
                """Invalidate the image texture so it will be regenerated."""
                # Clear the style hash to force regeneration check
                if hasattr(img, "_texture_style_hash"):
                    delattr(img, "_texture_style_hash")
                # Clear async load state so it will reload
                img._async_load_requested = False
                # Delete texture if it exists (will be recreated on next render)
                if hasattr(img, "_texture_id") and img._texture_id:
                    from pyPhotoAlbum.gl_imports import glDeleteTextures

                    try:
                        glDeleteTextures([img._texture_id])
                    except Exception:
                        pass  # GL context might not be available
                    delattr(img, "_texture_id")

            def execute(self):
                for img, _ in self.old_styles:
                    self.new_style_updater(img.style)
                    self._invalidate_texture(img)

            def undo(self):
                for img, old_style in self.old_styles:
                    img.style = old_style.copy()
                    self._invalidate_texture(img)

            def redo(self):
                self.execute()

            def serialize(self):
                # Style changes are not serialized (session-only undo)
                return {"type": "style_change", "description": self.description}

            @staticmethod
            def deserialize(data, project):
                # Style changes cannot be deserialized (session-only)
                return None

        cmd = StyleChangeCommand(old_styles, style_updater, description)
        self.project.history.execute(cmd)  # type: ignore[attr-defined]

        self.update_view()  # type: ignore[attr-defined]
        self.show_status(f"{description} applied to {len(images)} image(s)", 2000)  # type: ignore[attr-defined]

    # =========================================================================
    # Corner Radius
    # =========================================================================

    @ribbon_action(
        label="Round Corners",
        tooltip="Set corner radius for selected images",
        tab="Style",
        group="Corners",
        requires_selection=True,
    )
    def show_corner_radius_dialog(self):
        """Show dialog to set corner radius"""
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        from pyPhotoAlbum.dialogs.style_dialogs import CornerRadiusDialog

        # Get current radius from first selected image
        current_radius = images[0].style.corner_radius

        dialog = CornerRadiusDialog(self, current_radius)
        if dialog.exec():
            new_radius = dialog.get_value()
            self._apply_style_change(
                lambda style: setattr(style, "corner_radius", new_radius),
                f"Set corner radius to {new_radius}%",
            )

    @ribbon_action(
        label="No Corners",
        tooltip="Remove rounded corners from selected images",
        tab="Style",
        group="Corners",
        requires_selection=True,
    )
    def remove_corner_radius(self):
        """Remove corner radius (set to 0)"""
        self._apply_style_change(
            lambda style: setattr(style, "corner_radius", 0.0),
            "Remove corner radius",
        )

    # =========================================================================
    # Borders
    # =========================================================================

    @ribbon_action(
        label="Border...",
        tooltip="Set border for selected images",
        tab="Style",
        group="Border",
        requires_selection=True,
    )
    def show_border_dialog(self):
        """Show dialog to configure border"""
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        from pyPhotoAlbum.dialogs.style_dialogs import BorderDialog

        # Get current border from first selected image
        current_style = images[0].style

        dialog = BorderDialog(self, current_style.border_width, current_style.border_color)
        if dialog.exec():
            width, color = dialog.get_values()

            def update_border(style):
                style.border_width = width
                style.border_color = color

            self._apply_style_change(update_border, f"Set border ({width}mm)")

    @ribbon_action(
        label="No Border",
        tooltip="Remove border from selected images",
        tab="Style",
        group="Border",
        requires_selection=True,
    )
    def remove_border(self):
        """Remove border (set width to 0)"""
        self._apply_style_change(
            lambda style: setattr(style, "border_width", 0.0),
            "Remove border",
        )

    # =========================================================================
    # Shadows
    # =========================================================================

    @ribbon_action(
        label="Shadow...",
        tooltip="Configure drop shadow for selected images",
        tab="Style",
        group="Effects",
        requires_selection=True,
    )
    def show_shadow_dialog(self):
        """Show dialog to configure drop shadow"""
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        from pyPhotoAlbum.dialogs.style_dialogs import ShadowDialog

        # Get current shadow settings from first selected image
        current_style = images[0].style

        dialog = ShadowDialog(
            self,
            current_style.shadow_enabled,
            current_style.shadow_offset,
            current_style.shadow_blur,
            current_style.shadow_color,
        )
        if dialog.exec():
            enabled, offset, blur, color = dialog.get_values()

            def update_shadow(style):
                style.shadow_enabled = enabled
                style.shadow_offset = offset
                style.shadow_blur = blur
                style.shadow_color = color

            self._apply_style_change(update_shadow, "Configure shadow")

    @ribbon_action(
        label="Toggle Shadow",
        tooltip="Toggle drop shadow on/off for selected images",
        tab="Style",
        group="Effects",
        requires_selection=True,
    )
    def toggle_shadow(self):
        """Toggle shadow enabled/disabled"""
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        # Toggle based on first selected image
        new_state = not images[0].style.shadow_enabled

        self._apply_style_change(
            lambda style: setattr(style, "shadow_enabled", new_state),
            "Enable shadow" if new_state else "Disable shadow",
        )

    # =========================================================================
    # Style Presets
    # =========================================================================

    @ribbon_action(
        label="Polaroid",
        tooltip="Apply Polaroid-style frame (white border, shadow)",
        tab="Style",
        group="Presets",
        requires_selection=True,
    )
    def apply_polaroid_style(self):
        """Apply Polaroid-style preset"""

        def apply_preset(style):
            style.corner_radius = 0.0
            style.border_width = 3.0  # 3mm white border
            style.border_color = (255, 255, 255)
            style.shadow_enabled = True
            style.shadow_offset = (2.0, 2.0)
            style.shadow_blur = 4.0
            style.shadow_color = (0, 0, 0, 100)

        self._apply_style_change(apply_preset, "Apply Polaroid style")

    @ribbon_action(
        label="Rounded",
        tooltip="Apply rounded photo style",
        tab="Style",
        group="Presets",
        requires_selection=True,
    )
    def apply_rounded_style(self):
        """Apply rounded corners preset"""

        def apply_preset(style):
            style.corner_radius = 10.0  # 10% rounded
            style.border_width = 0.0
            style.shadow_enabled = True
            style.shadow_offset = (1.5, 1.5)
            style.shadow_blur = 3.0
            style.shadow_color = (0, 0, 0, 80)

        self._apply_style_change(apply_preset, "Apply rounded style")

    @ribbon_action(
        label="Clear Style",
        tooltip="Remove all styling from selected images",
        tab="Style",
        group="Presets",
        requires_selection=True,
    )
    def clear_style(self):
        """Remove all styling (reset to defaults)"""

        def clear_all(style):
            style.corner_radius = 0.0
            style.border_width = 0.0
            style.border_color = (0, 0, 0)
            style.shadow_enabled = False
            style.shadow_offset = (2.0, 2.0)
            style.shadow_blur = 3.0
            style.shadow_color = (0, 0, 0, 128)
            style.frame_style = None
            style.frame_color = (0, 0, 0)
            style.frame_corners = (True, True, True, True)

        self._apply_style_change(clear_all, "Clear style")

    # =========================================================================
    # Decorative Frames
    # =========================================================================

    @ribbon_action(
        label="Frame...",
        tooltip="Add decorative frame to selected images",
        tab="Style",
        group="Frame",
        requires_selection=True,
    )
    def show_frame_picker(self):
        """Show dialog to select decorative frame"""
        images = self._get_selected_images()
        if not images:
            self.show_status("No images selected", 2000)  # type: ignore[attr-defined]
            return

        from pyPhotoAlbum.dialogs.frame_picker_dialog import FramePickerDialog

        # Get current frame settings from first selected image
        current_style = images[0].style

        dialog = FramePickerDialog(
            self,
            current_frame=current_style.frame_style,
            current_color=current_style.frame_color,
            current_corners=current_style.frame_corners,
        )
        if dialog.exec():
            frame_name, color, corners = dialog.get_values()

            def update_frame(style):
                style.frame_style = frame_name
                style.frame_color = color
                style.frame_corners = corners

            desc = f"Apply frame '{frame_name}'" if frame_name else "Remove frame"
            self._apply_style_change(update_frame, desc)

    @ribbon_action(
        label="Remove Frame",
        tooltip="Remove decorative frame from selected images",
        tab="Style",
        group="Frame",
        requires_selection=True,
    )
    def remove_frame(self):
        """Remove decorative frame"""

        def clear_frame(style):
            style.frame_style = None
            style.frame_color = (0, 0, 0)
            style.frame_corners = (True, True, True, True)

        self._apply_style_change(clear_frame, "Remove frame")
