"""Playlist persistence module for music player.

This module provides data structures and persistence functionality for
saving and loading playlists across application sessions, following
the coding standards defined in coding-standards.md.
"""

from __future__ import annotations

import json
import logging
from dataclasses import asdict, dataclass, field
from datetime import datetime
from functools import cached_property
from pathlib import Path

# 避免循环导入, 使用字符串类型提示
# from pytola.multimedia.musicplayer.musicplayer import AudioTrack

logger = logging.getLogger(__name__)

PLAYLIST_CONFIG_FILE = Path.home() / ".pytola" / "musicplayer_playlists.json"


@dataclass
class SavedPlaylistTrack:
    """Data class representing a saved playlist track reference."""

    file_path: str
    title: str
    artist: str
    album: str
    duration: float
    file_size: int
    added_at: str = field(default_factory=lambda: datetime.now().isoformat())

    @classmethod
    def from_audio_track(cls, track) -> SavedPlaylistTrack:
        """Create SavedPlaylistTrack from AudioTrack-like object."""
        return cls(
            file_path=str(track.file_path),
            title=track.title,
            artist=track.artist,
            album=track.album,
            duration=track.duration,
            file_size=track.file_size,
        )


@dataclass
class SavedPlaylist:
    """Data class representing a saved playlist with persistence metadata."""

    name: str
    tracks: list[SavedPlaylistTrack] = field(default_factory=list)
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    modified_at: str = field(default_factory=lambda: datetime.now().isoformat())
    play_count: int = 0
    last_played_at: str | None = None

    def add_track(self, track) -> None:
        """Add a track to the playlist."""
        saved_track = SavedPlaylistTrack.from_audio_track(track)
        self.tracks.append(saved_track)
        self.modified_at = datetime.now().isoformat()

    def remove_track(self, index: int) -> bool:
        """Remove track at specified index."""
        if 0 <= index < len(self.tracks):
            self.tracks.pop(index)
            self.modified_at = datetime.now().isoformat()
            return True
        return False

    def clear(self) -> None:
        """Remove all tracks from playlist."""
        self.tracks.clear()
        self.modified_at = datetime.now().isoformat()

    @cached_property
    def total_duration(self) -> float:
        """Calculate total duration of all tracks in playlist."""
        return sum(track.duration for track in self.tracks)

    @cached_property
    def track_count(self) -> int:
        """Get number of tracks in playlist."""
        return len(self.tracks)


class PlaylistPersistenceManager:
    """Manager class for playlist persistence following coding standards.

    This class handles saving and loading playlists to/from JSON files,
    using the standard configuration location and dataclass patterns.
    """

    def __init__(self, config_file: Path | None = None) -> None:
        """Initialize playlist persistence manager.

        Args:
            config_file: Optional custom path for playlist configuration file.
                       If None, uses default location ~/.pytola/musicplayer_playlists.json
        """
        if config_file is not None:
            self.config_file = config_file
        else:
            self.config_file = PLAYLIST_CONFIG_FILE

        self._playlists: dict[str, SavedPlaylist] = {}
        self._load_playlists()

    def _load_playlists(self) -> None:
        """Load playlists from configuration file."""
        if not self.config_file.exists():
            logger.info(f"Playlist config file not found: {self.config_file}")
            return

        try:
            with Path(self.config_file).open(encoding="utf-8") as f:
                data = json.load(f)

            # Parse saved playlists
            for name, playlist_data in data.get("playlists", {}).items():
                tracks = [SavedPlaylistTrack(**track_data) for track_data in playlist_data.get("tracks", [])]

                self._playlists[name] = SavedPlaylist(
                    name=name,
                    tracks=tracks,
                    created_at=playlist_data.get("created_at", ""),
                    modified_at=playlist_data.get("modified_at", ""),
                    play_count=playlist_data.get("play_count", 0),
                    last_played_at=playlist_data.get("last_played_at"),
                )

            logger.info(
                f"Loaded {len(self._playlists)} playlists from: {self.config_file}",
            )

        except (json.JSONDecodeError, TypeError, ValueError, KeyError) as e:
            logger.exception(f"Failed to load playlists from {self.config_file}: {e}")

    def save_playlists(self) -> None:
        """Save all playlists to configuration file."""
        try:
            # Ensure config directory exists
            self.config_file.parent.mkdir(parents=True, exist_ok=True)

            # Convert playlists to serializable format
            playlists_data = {}
            for name, playlist in self._playlists.items():
                playlists_data[name] = {
                    "name": playlist.name,
                    "tracks": [asdict(track) for track in playlist.tracks],
                    "created_at": playlist.created_at,
                    "modified_at": playlist.modified_at,
                    "play_count": playlist.play_count,
                    "last_played_at": playlist.last_played_at,
                }

            # Create complete data structure
            data = {
                "playlists": playlists_data,
                "saved_at": datetime.now().isoformat(),
                "version": "1.0",
            }

            # Write to file
            with Path(self.config_file).open("w", encoding="utf-8") as f:
                json.dump(data, f, indent=4, ensure_ascii=False)

            logger.debug(f"Playlists saved to: {self.config_file}")

        except Exception as e:
            logger.exception(f"Failed to save playlists to {self.config_file}: {e}")

    def create_playlist(self, name: str) -> SavedPlaylist:
        """Create a new playlist.

        Args:
            name: Playlist name

        Returns
        -------
            Created playlist object
        """
        if name in self._playlists:
            logger.warning(f"Playlist '{name}' already exists")
            return self._playlists[name]

        playlist = SavedPlaylist(name=name)
        self._playlists[name] = playlist
        self.save_playlists()
        logger.info(f"Created playlist: {name}")
        return playlist

    def delete_playlist(self, name: str) -> bool:
        """Delete a playlist.

        Args:
            name: Playlist name to delete

        Returns
        -------
            True if deleted successfully, False if playlist not found
        """
        if name in self._playlists:
            del self._playlists[name]
            self.save_playlists()
            logger.info(f"Deleted playlist: {name}")
            return True
        return False

    def get_playlist(self, name: str) -> SavedPlaylist | None:
        """Get playlist by name.

        Args:
            name: Playlist name

        Returns
        -------
            Playlist object or None if not found
        """
        return self._playlists.get(name)

    def get_all_playlists(self) -> dict[str, SavedPlaylist]:
        """Get all playlists.

        Returns
        -------
            Dictionary of all playlists by name
        """
        return self._playlists.copy()

    def add_track_to_playlist(self, playlist_name: str, track) -> bool:
        """Add a track to specified playlist.

        Args:
            playlist_name: Name of playlist to add track to
            track: AudioTrack-like object to add

        Returns
        -------
            True if track added successfully, False if playlist not found
        """
        playlist = self._playlists.get(playlist_name)
        if playlist:
            playlist.add_track(track)
            self.save_playlists()
            logger.info(f"Added track '{track.title}' to playlist '{playlist_name}'")
            return True
        return False

    def remove_track_from_playlist(self, playlist_name: str, index: int) -> bool:
        """Remove track from specified playlist.

        Args:
            playlist_name: Name of playlist to remove track from
            index: Index of track to remove

        Returns
        -------
            True if track removed successfully, False if playlist or index invalid
        """
        playlist = self._playlists.get(playlist_name)
        if playlist:
            result = playlist.remove_track(index)
            if result:
                self.save_playlists()
                logger.info(
                    f"Removed track at index {index} from playlist '{playlist_name}'",
                )
            return result
        return False

    def update_playlist_play_stats(
        self,
        playlist_name: str,
        played: bool = True,
    ) -> None:
        """Update playlist play statistics.

        Args:
            playlist_name: Name of playlist
            played: Whether the playlist was played (default True)
        """
        playlist = self._playlists.get(playlist_name)
        if playlist:
            if played:
                playlist.play_count += 1
                playlist.last_played_at = datetime.now().isoformat()
                playlist.modified_at = datetime.now().isoformat()
            self.save_playlists()

    @cached_property
    def config_directory(self) -> Path:
        """Get configuration directory path.

        Returns
        -------
            Path to configuration directory
        """
        return self.config_file.parent

    def export_playlist(self, playlist_name: str, export_path: Path) -> bool:
        """Export playlist to a separate file.

        Args:
            playlist_name: Name of playlist to export
            export_path: Path to export file

        Returns
        -------
            True if exported successfully, False otherwise
        """
        playlist = self._playlists.get(playlist_name)
        if not playlist:
            logger.error(f"Playlist '{playlist_name}' not found for export")
            return False

        try:
            export_path.parent.mkdir(parents=True, exist_ok=True)

            data = {
                "name": playlist.name,
                "tracks": [asdict(track) for track in playlist.tracks],
                "created_at": playlist.created_at,
                "modified_at": playlist.modified_at,
                "play_count": playlist.play_count,
                "last_played_at": playlist.last_played_at,
                "exported_at": datetime.now().isoformat(),
                "version": "1.0",
            }

            with Path(export_path).open("w", encoding="utf-8") as f:
                json.dump(data, f, indent=4, ensure_ascii=False)

            logger.info(f"Exported playlist '{playlist_name}' to: {export_path}")
            return True

        except Exception as e:
            logger.exception(f"Failed to export playlist '{playlist_name}': {e}")
            return False

    def import_playlist(self, import_path: Path) -> SavedPlaylist | None:
        """Import playlist from a file.

        Args:
            import_path: Path to import file

        Returns
        -------
            Imported playlist object or None if import failed
        """
        if not import_path.exists():
            logger.error(f"Import file not found: {import_path}")
            return None

        try:
            with Path(import_path).open(encoding="utf-8") as f:
                data = json.load(f)

            # Check if playlist name already exists
            name = data.get("name", "Imported Playlist")
            if name in self._playlists:
                # Generate unique name
                counter = 1
                original_name = name
                while name in self._playlists:
                    name = f"{original_name} ({counter})"
                    counter += 1

            # Create playlist
            playlist = SavedPlaylist(
                name=name,
                created_at=data.get("created_at", datetime.now().isoformat()),
                modified_at=data.get("modified_at", datetime.now().isoformat()),
                play_count=data.get("play_count", 0),
                last_played_at=data.get("last_played_at"),
            )

            # Add tracks
            for track_data in data.get("tracks", []):
                playlist.tracks.append(SavedPlaylistTrack(**track_data))

            self._playlists[name] = playlist
            self.save_playlists()

            logger.info(f"Imported playlist '{name}' from: {import_path}")
            return playlist

        except (json.JSONDecodeError, TypeError, ValueError, KeyError) as e:
            logger.exception(f"Failed to import playlist from {import_path}: {e}")
            return None
