"""
Unit tests for settings validation in SettingsManager.

Tests the _validate_setting_value method to ensure proper input validation
for all configurable settings that could cause crashes or accessibility issues.
"""

import pytest
import sys
from pathlib import Path

# Import the settings manager
from fenrirscreenreader.core.settingsManager import SettingsManager


@pytest.mark.unit
@pytest.mark.settings
class TestSpeechSettingsValidation:
    """Test validation of speech-related settings."""

    def setup_method(self):
        """Create a SettingsManager instance for each test."""
        self.manager = SettingsManager()

    def test_speech_rate_valid_range(self):
        """Speech rate should accept values between 0.0 and 3.0."""
        # Valid boundary values
        self.manager._validate_setting_value("speech", "rate", 0.0)
        self.manager._validate_setting_value("speech", "rate", 1.5)
        self.manager._validate_setting_value("speech", "rate", 3.0)

    def test_speech_rate_rejects_negative(self):
        """Speech rate should reject negative values."""
        with pytest.raises(ValueError, match="must be between 0.0 and 3.0"):
            self.manager._validate_setting_value("speech", "rate", -0.1)

    def test_speech_rate_rejects_too_high(self):
        """Speech rate should reject values above 3.0."""
        with pytest.raises(ValueError, match="must be between 0.0 and 3.0"):
            self.manager._validate_setting_value("speech", "rate", 10.0)

    def test_speech_pitch_valid_range(self):
        """Speech pitch should accept values between 0.0 and 2.0."""
        self.manager._validate_setting_value("speech", "pitch", 0.0)
        self.manager._validate_setting_value("speech", "pitch", 1.0)
        self.manager._validate_setting_value("speech", "pitch", 2.0)

    def test_speech_pitch_rejects_invalid(self):
        """Speech pitch should reject out-of-range values."""
        with pytest.raises(ValueError, match="must be between 0.0 and 2.0"):
            self.manager._validate_setting_value("speech", "pitch", -1.0)
        with pytest.raises(ValueError, match="must be between 0.0 and 2.0"):
            self.manager._validate_setting_value("speech", "pitch", 5.0)

    def test_speech_volume_valid_range(self):
        """Speech volume should accept values between 0.0 and 1.5."""
        self.manager._validate_setting_value("speech", "volume", 0.0)
        self.manager._validate_setting_value("speech", "volume", 1.0)
        self.manager._validate_setting_value("speech", "volume", 1.5)

    def test_speech_volume_rejects_negative(self):
        """Speech volume should reject negative values."""
        with pytest.raises(ValueError, match="must be between 0.0 and 1.5"):
            self.manager._validate_setting_value("speech", "volume", -0.5)

    def test_speech_driver_whitelisted(self):
        """Speech driver should only accept whitelisted values."""
        # Valid drivers
        self.manager._validate_setting_value("speech", "driver", "speechdDriver")
        self.manager._validate_setting_value("speech", "driver", "genericDriver")
        self.manager._validate_setting_value("speech", "driver", "dummyDriver")

        # Invalid driver
        with pytest.raises(ValueError, match="Invalid speech driver"):
            self.manager._validate_setting_value(
                "speech", "driver", "nonexistentDriver"
            )


@pytest.mark.unit
@pytest.mark.settings
class TestSoundSettingsValidation:
    """Test validation of sound-related settings."""

    def setup_method(self):
        """Create a SettingsManager instance for each test."""
        self.manager = SettingsManager()

    def test_sound_volume_valid_range(self):
        """Sound volume should accept values between 0.0 and 1.5."""
        self.manager._validate_setting_value("sound", "volume", 0.0)
        self.manager._validate_setting_value("sound", "volume", 0.7)
        self.manager._validate_setting_value("sound", "volume", 1.5)

    def test_sound_volume_rejects_invalid(self):
        """Sound volume should reject out-of-range values."""
        with pytest.raises(ValueError, match="must be between 0.0 and 1.5"):
            self.manager._validate_setting_value("sound", "volume", -0.1)
        with pytest.raises(ValueError, match="must be between 0.0 and 1.5"):
            self.manager._validate_setting_value("sound", "volume", 2.0)

    def test_sound_driver_whitelisted(self):
        """Sound driver should only accept whitelisted values."""
        # Valid drivers
        self.manager._validate_setting_value("sound", "driver", "genericDriver")
        self.manager._validate_setting_value("sound", "driver", "gstreamerDriver")
        self.manager._validate_setting_value("sound", "driver", "dummyDriver")

        # Invalid driver
        with pytest.raises(ValueError, match="Invalid sound driver"):
            self.manager._validate_setting_value("sound", "driver", "invalidDriver")


@pytest.mark.unit
@pytest.mark.settings
class TestDriverValidation:
    """Test validation of driver selection settings."""

    def setup_method(self):
        """Create a SettingsManager instance for each test."""
        self.manager = SettingsManager()

    def test_screen_driver_whitelisted(self):
        """Screen driver should only accept whitelisted values."""
        # Valid drivers
        self.manager._validate_setting_value("screen", "driver", "vcsaDriver")
        self.manager._validate_setting_value("screen", "driver", "ptyDriver")
        self.manager._validate_setting_value("screen", "driver", "dummyDriver")

        # Invalid driver
        with pytest.raises(ValueError, match="Invalid screen driver"):
            self.manager._validate_setting_value("screen", "driver", "unknownDriver")

    def test_keyboard_driver_whitelisted(self):
        """Keyboard driver should only accept whitelisted values."""
        # Valid drivers
        self.manager._validate_setting_value("keyboard", "driver", "evdevDriver")
        self.manager._validate_setting_value("keyboard", "driver", "ptyDriver")
        self.manager._validate_setting_value("keyboard", "driver", "atspiDriver")
        self.manager._validate_setting_value("keyboard", "driver", "dummyDriver")

        # Invalid driver
        with pytest.raises(ValueError, match="Invalid input driver"):
            self.manager._validate_setting_value("keyboard", "driver", "badDriver")


@pytest.mark.unit
@pytest.mark.settings
class TestGeneralSettingsValidation:
    """Test validation of general settings."""

    def setup_method(self):
        """Create a SettingsManager instance for each test."""
        self.manager = SettingsManager()

    def test_debug_level_valid_range(self):
        """Debug level should accept values 0-3."""
        self.manager._validate_setting_value("general", "debug_level", 0)
        self.manager._validate_setting_value("general", "debug_level", 1)
        self.manager._validate_setting_value("general", "debug_level", 2)
        self.manager._validate_setting_value("general", "debug_level", 3)

    def test_debug_level_rejects_invalid(self):
        """Debug level should reject values outside 0-3."""
        with pytest.raises(ValueError, match="must be between 0 and 3"):
            self.manager._validate_setting_value("general", "debug_level", -1)
        with pytest.raises(ValueError, match="must be between 0 and 3"):
            self.manager._validate_setting_value("general", "debug_level", 10)


@pytest.mark.unit
@pytest.mark.settings
class TestValidationSkipsUnknownSettings:
    """Test that validation doesn't error on unknown settings."""

    def setup_method(self):
        """Create a SettingsManager instance for each test."""
        self.manager = SettingsManager()

    def test_unknown_section_no_error(self):
        """Unknown sections should not raise errors during validation."""
        # Should not raise - validation only applies to known critical settings
        self.manager._validate_setting_value("unknown_section", "setting", "value")

    def test_unknown_setting_no_error(self):
        """Unknown settings in known sections should not raise errors."""
        # Should not raise - only specific critical settings are validated
        self.manager._validate_setting_value("speech", "unknown_setting", "value")
