"""Unit tests for playlist manager module."""

from __future__ import annotations

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

from pytola.multimedia.musicplayer.musicplayer import AudioTrack, Playlist, PlaylistManager
from pytola.multimedia.musicplayer.playlist_persistence import PlaylistPersistenceManager


class TestPlaylist:
    """Test suite for Playlist class."""

    def setup_method(self) -> None:
        """Set up test environment."""
        self.playlist = Playlist(name="Test Playlist")

    def test_playlist_initialization(self) -> None:
        """Test playlist initialization."""
        assert self.playlist.name == "Test Playlist"
        assert len(self.playlist.tracks) == 0
        assert self.playlist.total_duration == 0.0
        assert self.playlist.track_count == 0

    def test_add_track(self) -> None:
        """Test adding tracks to playlist."""
        track = AudioTrack(
            file_path=Path("/test/file.mp3"),
            title="Test Track",
            artist="Test Artist",
            album="Test Album",
            duration=180.0,
            file_size=1024,
        )

        self.playlist.add_track(track)

        assert len(self.playlist.tracks) == 1
        assert self.playlist.track_count == 1
        assert self.playlist.tracks[0] == track
        assert self.playlist.total_duration == 180.0

    def test_remove_track(self) -> None:
        """Test removing tracks from playlist."""
        # Add some tracks
        track1 = AudioTrack(
            file_path=Path("/test/file1.mp3"),
            title="Track 1",
            artist="Artist 1",
            album="Album 1",
            duration=120.0,
            file_size=1024,
        )
        track2 = AudioTrack(
            file_path=Path("/test/file2.mp3"),
            title="Track 2",
            artist="Artist 2",
            album="Album 2",
            duration=180.0,
            file_size=2048,
        )

        self.playlist.add_track(track1)
        self.playlist.add_track(track2)

        # Remove valid index
        result = self.playlist.remove_track(0)
        assert result is True
        assert len(self.playlist.tracks) == 1
        assert self.playlist.tracks[0].title == "Track 2"

        # Remove invalid index
        result = self.playlist.remove_track(5)
        assert result is False
        assert len(self.playlist.tracks) == 1

    def test_move_track(self) -> None:
        """Test moving tracks within playlist."""
        # Add some tracks
        track1 = AudioTrack(
            file_path=Path("/test/file1.mp3"),
            title="Track 1",
            artist="Artist 1",
            album="Album 1",
            duration=120.0,
            file_size=1024,
        )
        track2 = AudioTrack(
            file_path=Path("/test/file2.mp3"),
            title="Track 2",
            artist="Artist 2",
            album="Album 2",
            duration=180.0,
            file_size=2048,
        )
        track3 = AudioTrack(
            file_path=Path("/test/file3.mp3"),
            title="Track 3",
            artist="Artist 3",
            album="Album 3",
            duration=240.0,
            file_size=3072,
        )

        self.playlist.add_track(track1)
        self.playlist.add_track(track2)
        self.playlist.add_track(track3)

        # Move track from index 0 to index 2
        result = self.playlist.move_track(0, 2)
        assert result is True
        assert self.playlist.tracks[0].title == "Track 2"
        assert self.playlist.tracks[1].title == "Track 3"
        assert self.playlist.tracks[2].title == "Track 1"

        # Test invalid indices
        result = self.playlist.move_track(-1, 0)
        assert result is False

        result = self.playlist.move_track(0, 10)
        assert result is False

    def test_clear_playlist(self) -> None:
        """Test clearing all tracks from playlist."""
        # Add some tracks
        track = AudioTrack(
            file_path=Path("/test/file.mp3"),
            title="Test Track",
            artist="Test Artist",
            album="Test Album",
            duration=180.0,
            file_size=1024,
        )

        self.playlist.add_track(track)
        assert len(self.playlist.tracks) == 1

        self.playlist.clear()
        assert len(self.playlist.tracks) == 0
        assert self.playlist.total_duration == 0.0
        assert self.playlist.track_count == 0

    def test_playlist_iteration(self) -> None:
        """Test playlist iteration."""
        track1 = AudioTrack(
            file_path=Path("/test/file1.mp3"),
            title="Track 1",
            artist="Artist 1",
            album="Album 1",
            duration=120.0,
            file_size=1024,
        )
        track2 = AudioTrack(
            file_path=Path("/test/file2.mp3"),
            title="Track 2",
            artist="Artist 2",
            album="Album 2",
            duration=180.0,
            file_size=2048,
        )

        self.playlist.add_track(track1)
        self.playlist.add_track(track2)

        # Test iteration
        tracks = list(self.playlist)
        assert len(tracks) == 2
        assert tracks[0] == track1
        assert tracks[1] == track2

    def test_cached_properties(self) -> None:
        """Test cached property invalidation."""
        track1 = AudioTrack(
            file_path=Path("/test/file1.mp3"),
            title="Track 1",
            artist="Artist 1",
            album="Album 1",
            duration=120.0,
            file_size=1024,
        )
        track2 = AudioTrack(
            file_path=Path("/test/file2.mp3"),
            title="Track 2",
            artist="Artist 2",
            album="Album 2",
            duration=180.0,
            file_size=2048,
        )

        # Initial cache
        assert self.playlist.total_duration == 0.0
        assert self.playlist.track_count == 0

        # Add tracks
        self.playlist.add_track(track1)
        self.playlist.add_track(track2)

        # Cache should be invalidated and recalculated
        assert self.playlist.total_duration == 300.0
        assert self.playlist.track_count == 2


class TestPlaylistManager:
    """Test suite for PlaylistManager class."""

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

        # Create PlaylistManager with isolated persistence
        persistence_manager = PlaylistPersistenceManager(config_file=self.temp_path)
        self.manager = PlaylistManager(persistence_manager=persistence_manager)

    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."""
        assert len(self.manager.playlists) == 0
        assert len(self.manager.recent_folders) == 0
        assert len(self.manager.get_supported_extensions()) == 5

    def test_create_playlist(self) -> None:
        """Test creating new playlists."""
        playlist = self.manager.create_playlist("My Playlist")
        assert playlist.name == "My Playlist"
        assert "My Playlist" in self.manager.playlists

        # Test creating duplicate playlist
        same_playlist = self.manager.create_playlist("My Playlist")
        assert same_playlist is playlist  # Should return existing playlist

    def test_delete_playlist(self) -> None:
        """Test deleting playlists."""
        self.manager.create_playlist("Test Playlist")
        assert "Test Playlist" in self.manager.playlists

        # Delete existing playlist
        result = self.manager.delete_playlist("Test Playlist")
        assert result is True
        assert "Test Playlist" not in self.manager.playlists

        # Delete non-existing playlist
        result = self.manager.delete_playlist("Nonexistent")
        assert result is False

    def test_get_playlist(self) -> None:
        """Test getting playlists."""
        playlist = self.manager.create_playlist("Test Playlist")

        # Get existing playlist
        result = self.manager.get_playlist("Test Playlist")
        assert result is playlist

        # Get non-existing playlist
        result = self.manager.get_playlist("Nonexistent")
        assert result is None

    def test_is_supported_file(self) -> None:
        """Test file format support checking."""
        # Test supported formats
        assert self.manager.is_supported_file(Path("test.mp3"))
        assert self.manager.is_supported_file(Path("test.wav"))
        assert self.manager.is_supported_file(Path("test.FLAC"))

        # Test unsupported formats
        assert not self.manager.is_supported_file(Path("test.txt"))
        assert not self.manager.is_supported_file(Path("test.pdf"))

    def test_scan_empty_folder(self) -> None:
        """Test scanning empty or invalid folder."""
        # Test non-existent folder
        tracks = self.manager.scan_folder_for_tracks(Path("/nonexistent/folder"))
        assert len(tracks) == 0

        # Test file instead of folder
        with tempfile.NamedTemporaryFile(suffix=".txt") as tmp_file:
            tracks = self.manager.scan_folder_for_tracks(Path(tmp_file.name))
            assert len(tracks) == 0

    def test_scan_folder_with_audio_files(self) -> None:
        """Test scanning folder with audio files."""
        # Create temporary directory structure
        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)

            # Create mock audio files
            mp3_file = tmp_path / "test.mp3"
            wav_file = tmp_path / "subdir" / "test.wav"
            txt_file = tmp_path / "readme.txt"

            mp3_file.touch()
            wav_file.parent.mkdir(exist_ok=True)
            wav_file.touch()
            txt_file.touch()

            # Mock the _extract_track_metadata method to return valid tracks
            mock_track = AudioTrack(
                file_path=mp3_file,
                title="Test MP3",
                artist="Test Artist",
                album="Test Album",
                duration=180.0,
                file_size=1024,
            )

            with patch.object(self.manager, "_extract_track_metadata", return_value=mock_track):
                tracks = self.manager.scan_folder_for_tracks(tmp_path)

                # Should find 2 audio files (MP3 and WAV)
                assert len(tracks) == 2

                # Verify recent folders updated
                assert len(self.manager.recent_folders) == 1
                assert self.manager.recent_folders[0] == tmp_path

    def test_extract_track_metadata_failure(self) -> None:
        """Test metadata extraction failure handling."""
        with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as tmp_file:
            tmp_path = Path(tmp_file.name)

        try:
            # Mock mutagen failure
            with patch("mutagen.File", side_effect=Exception("Mutagen error")):
                track = self.manager._extract_track_metadata(tmp_path)
                assert track is None
        finally:
            tmp_path.unlink()

    def test_add_folder_to_playlist(self) -> None:
        """Test adding folder contents to playlist."""
        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)

            # Create mock audio file
            mp3_file = tmp_path / "test.mp3"
            mp3_file.touch()

            # Mock successful metadata extraction
            mock_track = AudioTrack(
                file_path=mp3_file,
                title="Test Track",
                artist="Test Artist",
                album="Test Album",
                duration=180.0,
                file_size=1024,
            )

            with patch.object(self.manager, "_extract_track_metadata", return_value=mock_track):
                # Add folder to new playlist
                count = self.manager.add_folder_to_playlist("New Playlist", tmp_path)

                assert count == 1
                playlist = self.manager.get_playlist("New Playlist")
                assert playlist is not None
                assert len(playlist.tracks) == 1
                assert playlist.tracks[0].title == "Test Track"
