"""
Command builders for different interaction types.

Each builder is responsible for:
1. Validating if a command should be created
2. Creating the appropriate command object
3. Logging the operation
"""

from abc import ABC, abstractmethod
from typing import Optional, Any
from pyPhotoAlbum.models import BaseLayoutElement
from .interaction_validators import InteractionChangeDetector


class CommandBuilder(ABC):
    """Base class for command builders."""

    def __init__(self, change_detector: Optional[InteractionChangeDetector] = None):
        self.change_detector = change_detector or InteractionChangeDetector()

    @abstractmethod
    def can_build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> bool:
        """
        Check if a command should be built based on state changes.

        Args:
            element: The element being modified
            start_state: Dict containing the initial state
            **kwargs: Additional context

        Returns:
            True if a command should be created
        """
        pass

    @abstractmethod
    def build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> Optional[Any]:
        """
        Build and return the command object.

        Args:
            element: The element being modified
            start_state: Dict containing the initial state
            **kwargs: Additional context

        Returns:
            Command object or None
        """
        pass

    def log_command(self, command_type: str, details: str):
        """Log command creation for debugging."""
        print(f"{command_type} command created: {details}")


class MoveCommandBuilder(CommandBuilder):
    """Builds MoveElementCommand objects."""

    def can_build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> bool:
        """Check if position changed significantly."""
        old_pos = start_state.get("position")
        if old_pos is None:
            return False

        new_pos = element.position
        return self.change_detector.detect_position_change(old_pos, new_pos) is not None

    def build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> Optional[Any]:
        """Build a MoveElementCommand."""
        old_pos = start_state.get("position")
        if old_pos is None:
            return None

        new_pos = element.position
        change_info = self.change_detector.detect_position_change(old_pos, new_pos)

        if change_info is None:
            return None

        from pyPhotoAlbum.commands import MoveElementCommand

        command = MoveElementCommand(element, old_pos, new_pos)

        self.log_command("Move", f"{old_pos} → {new_pos}")
        return command


class ResizeCommandBuilder(CommandBuilder):
    """Builds ResizeElementCommand objects."""

    def can_build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> bool:
        """Check if position or size changed significantly."""
        old_pos = start_state.get("position")
        old_size = start_state.get("size")

        if old_pos is None or old_size is None:
            return False

        new_pos = element.position
        new_size = element.size

        pos_change = self.change_detector.detect_position_change(old_pos, new_pos)
        size_change = self.change_detector.detect_size_change(old_size, new_size)

        return pos_change is not None or size_change is not None

    def build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> Optional[Any]:
        """Build a ResizeElementCommand."""
        old_pos = start_state.get("position")
        old_size = start_state.get("size")

        if old_pos is None or old_size is None:
            return None

        new_pos = element.position
        new_size = element.size

        if not self.can_build(element, start_state):
            return None

        from pyPhotoAlbum.commands import ResizeElementCommand

        command = ResizeElementCommand(element, old_pos, old_size, new_pos, new_size)

        self.log_command("Resize", f"{old_size} → {new_size}")
        return command


class RotateCommandBuilder(CommandBuilder):
    """Builds RotateElementCommand objects."""

    def can_build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> bool:
        """Check if rotation changed significantly."""
        old_rotation = start_state.get("rotation")
        if old_rotation is None:
            return False

        new_rotation = element.rotation
        return self.change_detector.detect_rotation_change(old_rotation, new_rotation) is not None

    def build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> Optional[Any]:
        """Build a RotateElementCommand."""
        old_rotation = start_state.get("rotation")
        if old_rotation is None:
            return None

        new_rotation = element.rotation
        change_info = self.change_detector.detect_rotation_change(old_rotation, new_rotation)

        if change_info is None:
            return None

        from pyPhotoAlbum.commands import RotateElementCommand

        command = RotateElementCommand(element, old_rotation, new_rotation)

        self.log_command("Rotation", f"{old_rotation:.1f}° → {new_rotation:.1f}°")
        return command


class ImagePanCommandBuilder(CommandBuilder):
    """Builds AdjustImageCropCommand objects for image panning."""

    def can_build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> bool:
        """Check if crop info changed significantly."""
        from pyPhotoAlbum.models import ImageData

        if not isinstance(element, ImageData):
            return False

        old_crop = start_state.get("crop_info")
        if old_crop is None:
            return False

        new_crop = element.crop_info
        change_detector = InteractionChangeDetector(threshold=0.001)
        return change_detector.detect_crop_change(old_crop, new_crop) is not None

    def build(self, element: BaseLayoutElement, start_state: dict, **kwargs) -> Optional[Any]:
        """Build an AdjustImageCropCommand."""
        from pyPhotoAlbum.models import ImageData

        if not isinstance(element, ImageData):
            return None

        old_crop = start_state.get("crop_info")
        if old_crop is None:
            return None

        new_crop = element.crop_info
        change_detector = InteractionChangeDetector(threshold=0.001)
        change_info = change_detector.detect_crop_change(old_crop, new_crop)

        if change_info is None:
            return None

        from pyPhotoAlbum.commands import AdjustImageCropCommand

        command = AdjustImageCropCommand(element, old_crop, new_crop)

        self.log_command("Image pan", f"{old_crop} → {new_crop}")
        return command
