"""
Decorators and validators for interaction change detection.
"""

from functools import wraps
from typing import Optional, Tuple, Any


def significant_change(threshold: float = 0.1):
    """
    Decorator that validates if a change is significant enough to warrant a command.

    Args:
        threshold: Minimum change magnitude to be considered significant

    Returns:
        None if change is insignificant, otherwise returns the command builder result
    """

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            if result is None:
                return None
            return result

        return wrapper

    return decorator


class ChangeValidator:
    """Validates whether changes are significant enough to create commands."""

    @staticmethod
    def position_changed(
        old_pos: Optional[Tuple[float, float]], new_pos: Optional[Tuple[float, float]], threshold: float = 0.1
    ) -> bool:
        """Check if position changed significantly."""
        if old_pos is None or new_pos is None:
            return False

        dx = abs(new_pos[0] - old_pos[0])
        dy = abs(new_pos[1] - old_pos[1])
        return dx > threshold or dy > threshold

    @staticmethod
    def size_changed(
        old_size: Optional[Tuple[float, float]], new_size: Optional[Tuple[float, float]], threshold: float = 0.1
    ) -> bool:
        """Check if size changed significantly."""
        if old_size is None or new_size is None:
            return False

        dw = abs(new_size[0] - old_size[0])
        dh = abs(new_size[1] - old_size[1])
        return dw > threshold or dh > threshold

    @staticmethod
    def rotation_changed(old_rotation: Optional[float], new_rotation: Optional[float], threshold: float = 0.1) -> bool:
        """Check if rotation changed significantly."""
        if old_rotation is None or new_rotation is None:
            return False

        return abs(new_rotation - old_rotation) > threshold

    @staticmethod
    def crop_changed(
        old_crop: Optional[Tuple[float, float, float, float]],
        new_crop: Optional[Tuple[float, float, float, float]],
        threshold: float = 0.001,
    ) -> bool:
        """Check if crop info changed significantly."""
        if old_crop is None or new_crop is None:
            return False

        if old_crop == new_crop:
            return False

        return any(abs(new_crop[i] - old_crop[i]) > threshold for i in range(4))


class InteractionChangeDetector:
    """Detects and quantifies changes in element properties."""

    def __init__(self, threshold: float = 0.1):
        self.threshold = threshold
        self.validator = ChangeValidator()

    def detect_position_change(self, old_pos: Tuple[float, float], new_pos: Tuple[float, float]) -> Optional[dict]:
        """
        Detect position change and return change info.

        Returns:
            Dict with change info if significant, None otherwise
        """
        if not self.validator.position_changed(old_pos, new_pos, self.threshold):
            return None

        return {
            "old_position": old_pos,
            "new_position": new_pos,
            "delta_x": new_pos[0] - old_pos[0],
            "delta_y": new_pos[1] - old_pos[1],
        }

    def detect_size_change(self, old_size: Tuple[float, float], new_size: Tuple[float, float]) -> Optional[dict]:
        """
        Detect size change and return change info.

        Returns:
            Dict with change info if significant, None otherwise
        """
        if not self.validator.size_changed(old_size, new_size, self.threshold):
            return None

        return {
            "old_size": old_size,
            "new_size": new_size,
            "delta_width": new_size[0] - old_size[0],
            "delta_height": new_size[1] - old_size[1],
        }

    def detect_rotation_change(self, old_rotation: float, new_rotation: float) -> Optional[dict]:
        """
        Detect rotation change and return change info.

        Returns:
            Dict with change info if significant, None otherwise
        """
        if not self.validator.rotation_changed(old_rotation, new_rotation, self.threshold):
            return None

        return {"old_rotation": old_rotation, "new_rotation": new_rotation, "delta_angle": new_rotation - old_rotation}

    def detect_crop_change(
        self, old_crop: Tuple[float, float, float, float], new_crop: Tuple[float, float, float, float]
    ) -> Optional[dict]:
        """
        Detect crop change and return change info.

        Returns:
            Dict with change info if significant, None otherwise
        """
        if not self.validator.crop_changed(old_crop, new_crop, threshold=0.001):
            return None

        return {"old_crop": old_crop, "new_crop": new_crop, "delta": tuple(new_crop[i] - old_crop[i] for i in range(4))}
