"""
Unit tests for interaction validators and change detection.
"""

import pytest
from pyPhotoAlbum.mixins.interaction_validators import ChangeValidator, InteractionChangeDetector


class TestChangeValidator:
    """Tests for ChangeValidator class."""

    def test_position_changed_significant(self):
        """Test that significant position changes are detected."""
        validator = ChangeValidator()
        old_pos = (0.0, 0.0)
        new_pos = (5.0, 5.0)

        assert validator.position_changed(old_pos, new_pos, threshold=0.1)

    def test_position_changed_insignificant(self):
        """Test that insignificant position changes are not detected."""
        validator = ChangeValidator()
        old_pos = (0.0, 0.0)
        new_pos = (0.05, 0.05)

        assert not validator.position_changed(old_pos, new_pos, threshold=0.1)

    def test_position_changed_none_values(self):
        """Test that None values return False."""
        validator = ChangeValidator()

        assert not validator.position_changed(None, (1.0, 1.0))
        assert not validator.position_changed((1.0, 1.0), None)
        assert not validator.position_changed(None, None)

    def test_size_changed_significant(self):
        """Test that significant size changes are detected."""
        validator = ChangeValidator()
        old_size = (100.0, 100.0)
        new_size = (150.0, 150.0)

        assert validator.size_changed(old_size, new_size, threshold=0.1)

    def test_size_changed_insignificant(self):
        """Test that insignificant size changes are not detected."""
        validator = ChangeValidator()
        old_size = (100.0, 100.0)
        new_size = (100.05, 100.05)

        assert not validator.size_changed(old_size, new_size, threshold=0.1)

    def test_rotation_changed_significant(self):
        """Test that significant rotation changes are detected."""
        validator = ChangeValidator()
        old_rotation = 0.0
        new_rotation = 45.0

        assert validator.rotation_changed(old_rotation, new_rotation, threshold=0.1)

    def test_rotation_changed_insignificant(self):
        """Test that insignificant rotation changes are not detected."""
        validator = ChangeValidator()
        old_rotation = 0.0
        new_rotation = 0.05

        assert not validator.rotation_changed(old_rotation, new_rotation, threshold=0.1)

    def test_crop_changed_significant(self):
        """Test that significant crop changes are detected."""
        validator = ChangeValidator()
        old_crop = (0.0, 0.0, 1.0, 1.0)
        new_crop = (0.1, 0.1, 0.9, 0.9)

        assert validator.crop_changed(old_crop, new_crop, threshold=0.001)

    def test_crop_changed_insignificant(self):
        """Test that insignificant crop changes are not detected."""
        validator = ChangeValidator()
        old_crop = (0.0, 0.0, 1.0, 1.0)
        new_crop = (0.0001, 0.0001, 1.0, 1.0)

        assert not validator.crop_changed(old_crop, new_crop, threshold=0.001)

    def test_crop_changed_identical(self):
        """Test that identical crop values return False."""
        validator = ChangeValidator()
        crop = (0.0, 0.0, 1.0, 1.0)

        assert not validator.crop_changed(crop, crop)


class TestInteractionChangeDetector:
    """Tests for InteractionChangeDetector class."""

    def test_detect_position_change_significant(self):
        """Test detecting significant position changes."""
        detector = InteractionChangeDetector(threshold=0.1)
        old_pos = (0.0, 0.0)
        new_pos = (5.0, 3.0)

        change = detector.detect_position_change(old_pos, new_pos)

        assert change is not None
        assert change["old_position"] == old_pos
        assert change["new_position"] == new_pos
        assert change["delta_x"] == 5.0
        assert change["delta_y"] == 3.0

    def test_detect_position_change_insignificant(self):
        """Test that insignificant position changes return None."""
        detector = InteractionChangeDetector(threshold=0.1)
        old_pos = (0.0, 0.0)
        new_pos = (0.05, 0.05)

        change = detector.detect_position_change(old_pos, new_pos)

        assert change is None

    def test_detect_size_change_significant(self):
        """Test detecting significant size changes."""
        detector = InteractionChangeDetector(threshold=0.1)
        old_size = (100.0, 100.0)
        new_size = (150.0, 120.0)

        change = detector.detect_size_change(old_size, new_size)

        assert change is not None
        assert change["old_size"] == old_size
        assert change["new_size"] == new_size
        assert change["delta_width"] == 50.0
        assert change["delta_height"] == 20.0

    def test_detect_rotation_change_significant(self):
        """Test detecting significant rotation changes."""
        detector = InteractionChangeDetector(threshold=0.1)
        old_rotation = 0.0
        new_rotation = 45.0

        change = detector.detect_rotation_change(old_rotation, new_rotation)

        assert change is not None
        assert change["old_rotation"] == old_rotation
        assert change["new_rotation"] == new_rotation
        assert change["delta_angle"] == 45.0

    def test_detect_crop_change_significant(self):
        """Test detecting significant crop changes."""
        detector = InteractionChangeDetector(threshold=0.1)
        old_crop = (0.0, 0.0, 1.0, 1.0)
        new_crop = (0.1, 0.1, 0.9, 0.9)

        change = detector.detect_crop_change(old_crop, new_crop)

        assert change is not None
        assert change["old_crop"] == old_crop
        assert change["new_crop"] == new_crop
        # Use approximate comparison for floating point
        assert abs(change["delta"][0] - 0.1) < 0.001
        assert abs(change["delta"][1] - 0.1) < 0.001
        assert abs(change["delta"][2] - (-0.1)) < 0.001
        assert abs(change["delta"][3] - (-0.1)) < 0.001

    def test_custom_threshold(self):
        """Test using custom threshold values."""
        detector = InteractionChangeDetector(threshold=5.0)
        old_pos = (0.0, 0.0)
        new_pos = (3.0, 3.0)

        # Should be insignificant with threshold of 5.0
        change = detector.detect_position_change(old_pos, new_pos)
        assert change is None

        # Change detector with smaller threshold
        detector2 = InteractionChangeDetector(threshold=1.0)
        change2 = detector2.detect_position_change(old_pos, new_pos)
        assert change2 is not None
