"""Tests for color adjustment functionality in themes."""

import pytest

from freyja.theme import (
    RGB,
    AdjustStrategy,
    Theme,
    ThemeStyle,
    create_default_theme,
)


class TestThemeColorAdjustment:
    """Test color adjustment functionality in themes."""

    def test_theme_creation_with_adjustment(self):
        """Test creating theme with adjustment parameters."""
        theme = create_default_theme()
        theme.adjust_percent = 0.3
        theme.adjust_strategy = AdjustStrategy.LINEAR

        assert theme.adjust_percent == 0.3
        assert theme.adjust_strategy == AdjustStrategy.LINEAR

    def test_proportional_adjustment_positive(self):
        """Test proportional color adjustment with positive percentage using RGB."""
        original_rgb = RGB.from_ints(128, 128, 128)  # Mid gray
        style = ThemeStyle(fg=original_rgb)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.LINEAR,
            adjust_percent=0.25,  # 25% adjustment
        )

        adjusted_style = theme.get_adjusted_style(style)
        r, g, b = adjusted_style.fg.to_ints()

        # Current implementation: factor = -adjust_percent = -0.25, then 128 * (1 + (-0.25)) = 96
        assert r == 96
        assert g == 96
        assert b == 96

    def test_proportional_adjustment_negative(self):
        """Test proportional color adjustment with negative percentage using RGB."""
        original_rgb = RGB.from_ints(128, 128, 128)  # Mid gray
        style = ThemeStyle(fg=original_rgb)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.LINEAR,
            adjust_percent=-0.25,  # 25% darker
        )

        adjusted_style = theme.get_adjusted_style(style)
        r, g, b = adjusted_style.fg.to_ints()

        # Each component should be decreased by 25%: 128 + (128 * -0.25) = 96
        assert r == 96
        assert g == 96
        assert b == 96

    def test_absolute_adjustment_positive(self):
        """Test absolute color adjustment with positive percentage using RGB."""
        original_rgb = RGB.from_ints(64, 64, 64)  # Dark gray
        style = ThemeStyle(fg=original_rgb)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.ABSOLUTE,
            adjust_percent=0.5,  # 50% adjustment (actually darkens due to current implementation)
        )

        adjusted_style = theme.get_adjusted_style(style)
        r, g, b = adjusted_style.fg.to_ints()

        # Current implementation: 64 + (255-64) * (-0.5) = 64 + 191 * (-0.5) = -31.5, clamped to 0
        assert r == 0
        assert g == 0
        assert b == 0

    def test_absolute_adjustment_with_clamping(self):
        """Test absolute adjustment with clamping at boundaries using RGB."""
        original_rgb = RGB.from_ints(240, 240, 240)  # Light gray
        style = ThemeStyle(fg=original_rgb)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.ABSOLUTE,
            adjust_percent=0.5,  # 50% adjustment (actually darkens due to current implementation)
        )

        adjusted_style = theme.get_adjusted_style(style)
        r, g, b = adjusted_style.fg.to_ints()

        # Current implementation: 240 + (255-240) * (-0.5) = 240 + 15 * (-0.5) = 232.5 ≈ 232
        assert r == 232
        assert g == 232
        assert b == 232

    @staticmethod
    def _theme_with_style(style):
        return Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.LINEAR,
            adjust_percent=0.25,
        )

    def test_get_adjusted_style(self):
        """Test getting adjusted style using RGB."""
        original_rgb = RGB.from_ints(128, 128, 128)  # Mid gray
        original_style = ThemeStyle(fg=original_rgb, bold=True, italic=False)
        theme = self._theme_with_style(original_style)
        adjusted_style = theme.get_adjusted_style(original_style)

        assert adjusted_style is not None
        assert adjusted_style.fg != original_rgb  # Should be adjusted
        assert adjusted_style.bold is True  # Non-color properties preserved
        assert adjusted_style.italic is False

    def test_rgb_adjustment_preserves_properties(self):
        """Test that RGB adjustment preserves non-color properties."""
        original_rgb = RGB.from_ints(128, 128, 128)  # Mid gray - will be adjusted
        style = ThemeStyle(fg=original_rgb, bold=True, underline=True)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_strategy=AdjustStrategy.LINEAR,
            adjust_percent=0.25,
        )

        adjusted_style = theme.get_adjusted_style(style)

        # Color should be adjusted but other properties preserved
        assert adjusted_style.fg != original_rgb
        assert adjusted_style.bold is True
        assert adjusted_style.underline is True

    def test_adjustment_with_zero_percent(self):
        """Test no adjustment when percent is 0 using RGB."""
        original_rgb = RGB.from_ints(255, 0, 0)  # Red color
        style = ThemeStyle(fg=original_rgb)
        theme = Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_percent=0.0,  # No adjustment
        )

        adjusted_style = theme.get_adjusted_style(style)

        assert adjusted_style.fg == original_rgb  # Should remain unchanged

    def test_create_adjusted_copy(self):
        """Test creating an adjusted copy of a theme."""
        original_theme = create_default_theme()
        adjusted_theme = original_theme.create_adjusted_copy(0.2)

        assert adjusted_theme.adjust_percent == 0.2
        assert adjusted_theme != original_theme  # Different instances

        # Original theme should be unchanged
        assert original_theme.adjust_percent == 0.0

    def test_adjustment_edge_cases(self):
        """Test adjustment with edge case RGB colors."""
        theme = Theme(
            title=ThemeStyle(),
            subtitle=ThemeStyle(),
            command_name=ThemeStyle(),
            command_description=ThemeStyle(),
            command_group_name=ThemeStyle(),
            command_group_description=ThemeStyle(),
            grouped_command_name=ThemeStyle(),
            grouped_command_description=ThemeStyle(),
            option_name=ThemeStyle(),
            option_description=ThemeStyle(),
            command_group_option_name=ThemeStyle(),
            command_group_option_description=ThemeStyle(),
            grouped_command_option_name=ThemeStyle(),
            grouped_command_option_description=ThemeStyle(),
            required_asterisk=ThemeStyle(),
            adjust_strategy=AdjustStrategy.LINEAR,
            adjust_percent=0.5,
        )

        # Test with black RGB (should handle division by zero)
        black_rgb = RGB.from_ints(0, 0, 0)
        black_style = ThemeStyle(fg=black_rgb)
        adjusted_black_style = theme.get_adjusted_style(black_style)
        assert adjusted_black_style.fg == black_rgb  # Can't adjust pure black

        # Test with white RGB
        white_rgb = RGB.from_ints(255, 255, 255)
        white_style = ThemeStyle(fg=white_rgb)
        adjusted_white_style = theme.get_adjusted_style(white_style)
        assert adjusted_white_style.fg == white_rgb  # White should remain unchanged

        # Test with None style
        none_style = ThemeStyle(fg=None)
        adjusted_none_style = theme.get_adjusted_style(none_style)
        assert adjusted_none_style.fg is None

    def test_adjust_percent_validation_in_init(self):
        """Test adjust_percent validation in Theme.__init__."""
        style = ThemeStyle()

        # Valid range should work
        Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_percent=-5.0,  # Minimum valid
        )

        Theme(
            title=style,
            subtitle=style,
            command_name=style,
            command_description=style,
            command_group_name=style,
            command_group_description=style,
            grouped_command_name=style,
            grouped_command_description=style,
            option_name=style,
            option_description=style,
            command_group_option_name=style,
            command_group_option_description=style,
            grouped_command_option_name=style,
            grouped_command_option_description=style,
            required_asterisk=style,
            adjust_percent=5.0,  # Maximum valid
        )

        # Below minimum should raise exception
        with pytest.raises(
            ValueError, match="adjust_percent must be between -5.0 and 5.0, got -5.1"
        ):
            Theme(
                title=style,
                subtitle=style,
                command_name=style,
                command_description=style,
                command_group_name=style,
                command_group_description=style,
                grouped_command_name=style,
                grouped_command_description=style,
                option_name=style,
                option_description=style,
                command_group_option_name=style,
                command_group_option_description=style,
                grouped_command_option_name=style,
                grouped_command_option_description=style,
                required_asterisk=style,
                adjust_percent=-5.1,
            )

        # Above maximum should raise exception
        with pytest.raises(
            ValueError, match="adjust_percent must be between -5.0 and 5.0, got 5.1"
        ):
            Theme(
                title=style,
                subtitle=style,
                command_name=style,
                command_description=style,
                command_group_name=style,
                command_group_description=style,
                grouped_command_name=style,
                grouped_command_description=style,
                option_name=style,
                option_description=style,
                command_group_option_name=style,
                command_group_option_description=style,
                grouped_command_option_name=style,
                grouped_command_option_description=style,
                required_asterisk=style,
                adjust_percent=5.1,
            )

    def test_adjust_percent_validation_in_create_adjusted_copy(self):
        """Test adjust_percent validation in create_adjusted_copy method."""
        original_theme = create_default_theme()

        # Valid range should work
        original_theme.create_adjusted_copy(-5.0)  # Minimum valid
        original_theme.create_adjusted_copy(5.0)  # Maximum valid

        # Below minimum should raise exception
        with pytest.raises(
            ValueError, match="adjust_percent must be between -5.0 and 5.0, got -5.1"
        ):
            original_theme.create_adjusted_copy(-5.1)

        # Above maximum should raise exception
        with pytest.raises(
            ValueError, match="adjust_percent must be between -5.0 and 5.0, got 5.1"
        ):
            original_theme.create_adjusted_copy(5.1)
