"""Unit tests for configuration manager module."""

from __future__ import annotations

import json
import tempfile
from pathlib import Path
from unittest.mock import patch

from pytola.multimedia.musicplayer.musicplayer import ConfigManager, MusicPlayerConfig


class TestMusicPlayerConfig:
    """Test suite for MusicPlayerConfig dataclass."""

    def test_config_initialization(self) -> None:
        """Test configuration initialization with default values."""
        config = MusicPlayerConfig()

        # Test default values
        assert config.window_width == 1000
        assert config.window_height == 700
        assert config.volume == 0.7
        assert config.theme == "light"
        assert config.repeat_mode == "none"
        assert config.shuffle_mode is False
        assert config.recent_folders == []
        assert config.recent_playlists == []

    def test_config_volume_validation(self) -> None:
        """Test volume value validation."""
        # Test normal values
        config = MusicPlayerConfig(volume=0.5, last_volume=0.3)
        assert config.volume == 0.5
        assert config.last_volume == 0.3

        # Test boundary clamping
        config = MusicPlayerConfig(volume=-0.1, last_volume=1.5)
        assert config.volume == 0.0
        assert config.last_volume == 1.0

    def test_config_post_init(self) -> None:
        """Test post-initialization validation."""
        # Test recent folders limitation
        many_folders = [f"/folder{i}" for i in range(15)]
        config = MusicPlayerConfig(recent_folders=many_folders)
        assert len(config.recent_folders) == 10  # Should be limited to 10

        # Test recent playlists limitation
        many_playlists = [f"playlist{i}" for i in range(10)]
        config = MusicPlayerConfig(recent_playlists=many_playlists)
        assert len(config.recent_playlists) == 5  # Should be limited to 5


class TestConfigManager:
    """Test suite for ConfigManager class."""

    def setup_method(self) -> None:
        """Set up test environment."""
        # Use temporary file for testing
        self.temp_file = tempfile.NamedTemporaryFile(suffix=".json", delete=False)
        self.temp_path = Path(self.temp_file.name)
        self.temp_file.close()

        self.manager = ConfigManager(config_file=self.temp_path)

    def teardown_method(self) -> None:
        """Cleanup after tests."""
        if self.temp_path.exists():
            self.temp_path.unlink()

    def test_manager_initialization(self) -> None:
        """Test manager initialization."""
        # Should load default config when file doesn't exist
        assert isinstance(self.manager.get_config(), MusicPlayerConfig)
        assert self.manager.config_file == self.temp_path

    def test_get_config(self) -> None:
        """Test getting configuration."""
        config = self.manager.get_config()
        assert isinstance(config, MusicPlayerConfig)
        assert config.window_width == 1000  # Default value

    def test_update_config(self) -> None:
        """Test updating configuration values."""
        # Update some values
        self.manager.update_config(window_width=1200, window_height=800, volume=0.8)

        config = self.manager.get_config()
        assert config.window_width == 1200
        assert config.window_height == 800
        assert config.volume == 0.8

    def test_update_config_invalid_key(self) -> None:
        """Test updating with invalid configuration key."""
        with patch("logging.Logger.warning") as mock_warning:
            self.manager.update_config(invalid_key="value")
            mock_warning.assert_called_once()

    def test_save_and_load_config(self) -> None:
        """Test saving and loading configuration."""
        # Modify configuration using update_config method
        self.manager.update_config(window_width=1500, volume=0.9)

        # Save configuration
        self.manager.save_config()

        # Create new manager to load from file
        new_manager = ConfigManager(config_file=self.temp_path)
        loaded_config = new_manager.get_config()

        # Verify values were loaded correctly
        assert loaded_config.window_width == 1500
        assert loaded_config.volume == 0.9

    def test_load_corrupted_config(self) -> None:
        """Test loading corrupted configuration file."""
        # Use separate temp file to avoid interference
        with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tmp_file:
            corrupt_path = Path(tmp_file.name)

        try:
            # Create corrupted JSON file
            with open(corrupt_path, "w", encoding="utf-8") as f:
                f.write("{ invalid json }")

            # Should fall back to defaults
            with patch("logging.Logger.error") as mock_error:
                manager = ConfigManager(config_file=corrupt_path)
                config = manager.get_config()

                assert isinstance(config, MusicPlayerConfig)
                assert config.window_width == 1000  # Default value
                mock_error.assert_called_once()
        finally:
            if corrupt_path.exists():
                corrupt_path.unlink()

    def test_load_config_with_missing_fields(self) -> None:
        """Test loading configuration with missing fields."""
        # Create partial config file
        partial_config = {
            "window_width": 800,
            "volume": 0.5,
            # Missing other fields
        }

        with open(self.temp_path, "w", encoding="utf-8") as f:
            json.dump(partial_config, f)

        manager = ConfigManager(config_file=self.temp_path)
        config = manager.get_config()

        # Should have default values for missing fields
        assert config.window_width == 800
        assert config.window_height == 700  # Default
        assert config.volume == 0.5

    def test_add_recent_folder(self) -> None:
        """Test adding folders to recent folders list."""
        folder1 = "/home/user/music"
        folder2 = "/home/user/downloads"

        self.manager.add_recent_folder(folder1)
        self.manager.add_recent_folder(folder2)

        recent = self.manager.get_recent_folders()
        assert len(recent) == 2
        assert recent[0] == folder2  # Most recent first
        assert recent[1] == folder1

        # Test duplicate handling
        self.manager.add_recent_folder(folder1)
        recent = self.manager.get_recent_folders()
        assert len(recent) == 2
        assert recent[0] == folder1  # Moved to front
        assert recent[1] == folder2

    def test_recent_folders_limit(self) -> None:
        """Test recent folders list size limitation."""
        # Add more than 10 folders
        for i in range(15):
            self.manager.add_recent_folder(f"/folder{i}")

        recent = self.manager.get_recent_folders()
        assert len(recent) == 10  # Should be limited to 10

    def test_add_recent_playlist(self) -> None:
        """Test adding playlists to recent playlists list."""
        playlist1 = "My Favorites"
        playlist2 = "Rock Classics"

        self.manager.add_recent_playlist(playlist1)
        self.manager.add_recent_playlist(playlist2)

        recent = self.manager.get_recent_playlists()
        assert len(recent) == 2
        assert recent[0] == playlist2  # Most recent first
        assert recent[1] == playlist1

    def test_recent_playlists_limit(self) -> None:
        """Test recent playlists list size limitation."""
        # Add more than 5 playlists
        for i in range(10):
            self.manager.add_recent_playlist(f"Playlist{i}")

        recent = self.manager.get_recent_playlists()
        assert len(recent) == 5  # Should be limited to 5

    def test_reset_to_defaults(self) -> None:
        """Test resetting configuration to defaults."""
        # Use separate manager to avoid test interference
        with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tmp_file:
            separate_path = Path(tmp_file.name)

        try:
            manager = ConfigManager(config_file=separate_path)

            # Modify configuration
            manager.update_config(window_width=2000, volume=0.1, theme="dark")

            # Reset to defaults
            manager.reset_to_defaults()

            config = manager.get_config()
            assert config.window_width == 1000  # Default
            assert config.volume == 0.7  # Default
            assert config.theme == "light"  # Default
        finally:
            if separate_path.exists():
                separate_path.unlink()

    def test_config_directory_property(self) -> None:
        """Test config directory property."""
        config_dir = self.manager.config_directory
        assert config_dir == self.temp_path.parent

    def test_backup_config(self) -> None:
        """Test configuration backup functionality."""
        # Modify and save config
        self.manager.update_config(window_width=1234)
        self.manager.save_config()

        # Create backup
        result = self.manager.backup_config()
        assert result is True

        # Find the actual backup file (it will have current timestamp)
        backup_files = list(self.temp_path.parent.glob("musicplayer_backup_*.json"))
        assert len(backup_files) >= 1, "No backup files found"

        # Use the most recent backup file
        backup_file = max(backup_files, key=lambda f: f.stat().st_mtime)

        # Verify backup content
        with open(backup_file, encoding="utf-8") as f:
            backup_data = json.load(f)
            assert backup_data["window_width"] == 1234

        # Cleanup backup file
        backup_file.unlink()

    def test_backup_config_custom_path(self) -> None:
        """Test backup with custom path."""
        with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tmp_file:
            backup_path = Path(tmp_file.name)

        try:
            result = self.manager.backup_config(backup_path)
            assert result is True
            assert backup_path.exists()
        finally:
            if backup_path.exists():
                backup_path.unlink()
