import os
import json
import uuid
import time
import logging
from typing import List, Dict, Optional

from xdg_base import xdg_data_path

logger = logging.getLogger(__name__)

_PLAYLIST_FILE = os.path.join(xdg_data_path(), "playlists.json")


class Playlist:
    def __init__(self, name: str, playlist_id: Optional[str] = None,
                 track_paths: Optional[List[str]] = None,
                 created_at: Optional[float] = None,
                 updated_at: Optional[float] = None):
        self.id = playlist_id or str(uuid.uuid4())
        self.name = name
        self.track_paths = track_paths or []
        self.created_at = created_at or time.time()
        self.updated_at = updated_at or time.time()

    def to_dict(self) -> Dict:
        return {
            "id": self.id,
            "name": self.name,
            "track_paths": self.track_paths,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
        }

    @classmethod
    def from_dict(cls, data: Dict) -> "Playlist":
        return cls(
            name=data["name"],
            playlist_id=data.get("id"),
            track_paths=data.get("track_paths", []),
            created_at=data.get("created_at"),
            updated_at=data.get("updated_at"),
        )


class PlaylistManager:
    def __init__(self, filepath: str = _PLAYLIST_FILE):
        self._filepath = filepath
        self._playlists: List[Playlist] = []
        self._migrate_old()
        self._load()

    def _migrate_old(self):
        """Migrate from old ~/.config/ location to ~/.local/share/."""
        old = os.path.join(
            os.path.expanduser("~"), ".config", "neo-pod-desktop", "playlists.json",
        )
        if os.path.exists(old) and not os.path.exists(self._filepath):
            try:
                os.makedirs(os.path.dirname(self._filepath), exist_ok=True)
                os.rename(old, self._filepath)
                logger.info("Migrated playlists from %s → %s", old, self._filepath)
            except OSError as exc:
                logger.warning("Playlist migration failed: %s", exc)

    def _load(self):
        try:
            os.makedirs(os.path.dirname(self._filepath), exist_ok=True)
            if os.path.exists(self._filepath):
                with open(self._filepath, "r", encoding="utf-8") as f:
                    data = json.load(f)
                self._playlists = [Playlist.from_dict(p) for p in data]
        except Exception as e:
            logger.error("Failed to load playlists: %s", e)
            self._playlists = []

    def save(self):
        try:
            os.makedirs(os.path.dirname(self._filepath), exist_ok=True)
            with open(self._filepath, "w", encoding="utf-8") as f:
                json.dump([p.to_dict() for p in self._playlists], f, indent=2)
        except Exception as e:
            logger.error("Failed to save playlists: %s", e)

    def create(self, name: str) -> str:
        playlist = Playlist(name=name)
        self._playlists.append(playlist)
        self.save()
        return playlist.id

    def rename(self, playlist_id: str, new_name: str) -> bool:
        for p in self._playlists:
            if p.id == playlist_id:
                p.name = new_name
                p.updated_at = time.time()
                self.save()
                return True
        return False

    def delete(self, playlist_id: str) -> bool:
        for i, p in enumerate(self._playlists):
            if p.id == playlist_id:
                del self._playlists[i]
                self.save()
                return True
        return False

    def add_tracks(self, playlist_id: str, paths: List[str]) -> bool:
        for p in self._playlists:
            if p.id == playlist_id:
                existing = set(p.track_paths)
                for path in paths:
                    if path not in existing:
                        p.track_paths.append(path)
                        existing.add(path)
                p.updated_at = time.time()
                self.save()
                return True
        return False

    def remove_tracks(self, playlist_id: str, paths: List[str]) -> bool:
        for p in self._playlists:
            if p.id == playlist_id:
                path_set = set(paths)
                p.track_paths = [t for t in p.track_paths if t not in path_set]
                p.updated_at = time.time()
                self.save()
                return True
        return False

    def reorder(self, playlist_ids: List[str]) -> bool:
        id_order = {pid: i for i, pid in enumerate(playlist_ids)}
        self._playlists.sort(key=lambda p: id_order.get(p.id, 999))
        self.save()
        return True

    def get_all(self) -> List[Playlist]:
        return list(self._playlists)

    def get_by_id(self, playlist_id: str) -> Optional[Playlist]:
        for p in self._playlists:
            if p.id == playlist_id:
                return p
        return None
