"""Tests for the colors module."""

import pytest
from unittest.mock import MagicMock, patch
import colorsys

# Import the pure functions that don't need iterm2
from maniple_mcp.colors import (
    GOLDEN_RATIO_CONJUGATE,
    DEFAULT_SATURATION,
    DEFAULT_LIGHTNESS,
    get_hue_for_index,
    hsl_to_rgb_tuple,
)


class TestGetHueForIndex:
    """Tests for the get_hue_for_index function."""

    def test_index_zero_returns_zero(self):
        """Index 0 should return hue of 0."""
        assert get_hue_for_index(0) == 0.0

    def test_index_one_returns_golden_ratio(self):
        """Index 1 should return the golden ratio conjugate."""
        expected = GOLDEN_RATIO_CONJUGATE
        assert abs(get_hue_for_index(1) - expected) < 1e-10

    def test_consecutive_indices_produce_distinct_hues(self):
        """Consecutive indices should produce visually distinct hues."""
        hues = [get_hue_for_index(i) for i in range(10)]
        # All hues should be unique
        assert len(set(hues)) == 10

        # Adjacent hues should differ by at least 0.1 (36 degrees) for visibility
        for i in range(len(hues) - 1):
            diff = abs(hues[i] - hues[i + 1])
            # Account for wraparound
            diff = min(diff, 1.0 - diff)
            assert diff > 0.1, f"Hues {hues[i]} and {hues[i+1]} are too similar"

    def test_hue_wraps_correctly(self):
        """Hue values should always be in [0, 1) range."""
        for i in range(100):
            hue = get_hue_for_index(i)
            assert 0.0 <= hue < 1.0, f"Hue {hue} for index {i} is out of range"

    def test_large_indices_still_produce_valid_hues(self):
        """Large indices should still produce valid hue values."""
        hue = get_hue_for_index(1000)
        assert 0.0 <= hue < 1.0


class TestHslToRgbTuple:
    """Tests for the hsl_to_rgb_tuple function."""

    def test_red_hue(self):
        """Hue 0 (red) should produce a reddish color."""
        r, g, b = hsl_to_rgb_tuple(0.0)
        assert r > g and r > b, "Red should be dominant for hue 0"

    def test_green_hue(self):
        """Hue 0.333 (green) should produce a greenish color."""
        r, g, b = hsl_to_rgb_tuple(0.333)
        assert g > r and g > b, "Green should be dominant for hue ~0.33"

    def test_blue_hue(self):
        """Hue 0.666 (blue) should produce a bluish color."""
        r, g, b = hsl_to_rgb_tuple(0.666)
        assert b > r and b > g, "Blue should be dominant for hue ~0.66"

    def test_rgb_values_in_valid_range(self):
        """RGB values should be integers in 0-255 range."""
        for hue in [0.0, 0.25, 0.5, 0.75]:
            r, g, b = hsl_to_rgb_tuple(hue)
            for value in (r, g, b):
                assert isinstance(value, int), "RGB values should be integers"
                assert 0 <= value <= 255, f"RGB value {value} out of range"

    def test_custom_saturation_and_lightness(self):
        """Custom saturation and lightness should affect output."""
        # Low saturation should produce more gray-ish colors
        low_sat = hsl_to_rgb_tuple(0.0, saturation=0.1, lightness=0.5)
        high_sat = hsl_to_rgb_tuple(0.0, saturation=0.9, lightness=0.5)

        # High saturation red should be more different from gray
        assert high_sat[0] - high_sat[1] > low_sat[0] - low_sat[1]


class TestGenerateTabColor:
    """Tests for the generate_tab_color function."""

    def test_returns_iterm2_color(self):
        """generate_tab_color should return an iterm2.Color object."""
        # Mock the iterm2 module and its color submodule
        mock_color_instance = MagicMock()
        mock_color_class = MagicMock(return_value=mock_color_instance)
        mock_color_module = MagicMock()
        mock_color_module.Color = mock_color_class
        mock_iterm2 = MagicMock()
        mock_iterm2.color = mock_color_module

        # Must patch both 'iterm2' and 'iterm2.color' for submodule imports
        with patch.dict('sys.modules', {
            'iterm2': mock_iterm2,
            'iterm2.color': mock_color_module,
        }):
            from maniple_mcp.colors import generate_tab_color
            result = generate_tab_color(0)

            # Verify Color was called with RGB values
            mock_color_class.assert_called_once()
            call_kwargs = mock_color_class.call_args[1]
            assert 'r' in call_kwargs
            assert 'g' in call_kwargs
            assert 'b' in call_kwargs

    def test_different_indices_produce_different_colors(self):
        """Different indices should produce different colors."""
        mock_colors = []

        def capture_color(**kwargs):
            color = MagicMock()
            color.rgb = (kwargs['r'], kwargs['g'], kwargs['b'])
            mock_colors.append(color)
            return color

        mock_color_class = MagicMock(side_effect=capture_color)
        mock_color_module = MagicMock()
        mock_color_module.Color = mock_color_class
        mock_iterm2 = MagicMock()
        mock_iterm2.color = mock_color_module

        # Must patch both 'iterm2' and 'iterm2.color' for submodule imports
        with patch.dict('sys.modules', {
            'iterm2': mock_iterm2,
            'iterm2.color': mock_color_module,
        }):
            from maniple_mcp.colors import generate_tab_color
            for i in range(5):
                generate_tab_color(i)

        # All colors should be unique
        rgb_values = [c.rgb for c in mock_colors]
        assert len(set(rgb_values)) == 5, "All generated colors should be unique"


class TestConstants:
    """Tests for module constants."""

    def test_golden_ratio_value(self):
        """Golden ratio conjugate should be approximately 0.618."""
        assert abs(GOLDEN_RATIO_CONJUGATE - 0.618033988749895) < 1e-15

    def test_default_saturation(self):
        """Default saturation should be 65%."""
        assert DEFAULT_SATURATION == 0.65

    def test_default_lightness(self):
        """Default lightness should be 55%."""
        assert DEFAULT_LIGHTNESS == 0.55
