
from src.Library import *

import json
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Any

def _dt_to_str(dt: Optional[datetime]) -> Optional[str]:
    return dt.isoformat() if dt else None


def _str_to_dt(s: Optional[str]) -> Optional[datetime]:
    return datetime.fromisoformat(s) if s else None


def _path_to_str(p: Optional[Path]) -> Optional[str]:
    return str(p) if p else None


def _str_to_path(s: Optional[str]) -> Optional[Path]:
    return Path(s) if s else None


class LibrarySaver:
    def __init__(self, library: Library):
        self.library = library

    def _entity_to_dict(self, entity: Entity) -> dict[str, Any]:
        return {
            "id": entity.id,
            "mbid": entity.mbid,

            "title": entity.title,
            "duration": entity.duration_ms,

            "fav" : entity.fav,
            "rating" : entity.rating,
            "play_count": entity.play_count,
            "date_added": entity.date_added,
            "date_released": entity.date_released,
            "date_last_play": entity.date_last_play,
            "auto_score": entity.auto_score,

            "local_id": entity.local_id,
            "local_path": entity.local_path,
            "resolved_percentage": entity.resolved_percentage,
        }

    def _artist_to_dict(self, artist: Artist) -> dict[str, Any]:
        return self._entity_to_dict(artist) | {
            "tracks": [track.id for track in artist.tracks],
            "albums": [album.id for album in artist.albums],
        }

    def _track_to_dict(self, track: Track) -> dict[str, Any]:
         return self._entity_to_dict(track) | {
            "artists": [artist.id for artist in track.artists],
            "album": track.album.id,
            "duration_ms": track.duration_ms,
            "has_lyrics": track.has_lyrics,
        }

    def _playlist_to_dict(self, playlist: Playlist) -> dict[str, Any]:
        return self._entity_to_dict(playlist) | {
            "tracks": [track.id for track in playlist.tracks],
            "description": playlist.description,
        }

    def _album_to_dict(self ,album: Album) -> dict[str, Any]:
        return self._entity_to_dict(album) | {
            "tracks": [track.id for track in album.tracks],
            "artists": [artist.id for artist in album.artists],
        }

    def save(self, path: Path) -> None:

        data = {
            "name": self.library.name,
            "tracks": [self._track_to_dict(track) for track in self.library.tracks],
            "albums": [self._album_to_dict(album) for album in self.library.albums],
            "artists": [self._artist_to_dict(artist) for artist in self.library.artists],
            "playlists": [self._playlist_to_dict(playlist) for playlist in self.library.playlists],
            "top_tracks": [track.id for track in self.library.top_tracks],
            "top_artists": [artist.id for artist in self.library.top_artists],
            "liked_tracks": [track.id for track in self.library.liked_tracks],
        }

        path.parent.mkdir(parents=True, exist_ok=True)
        with path.open("w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)



class LibraryLoader:
    def __init__(self):
        self.track_map: dict[str, Track] = {}
        self.album_map: dict[str, Album] = {}
        self.artist_map: dict[str, Artist] = {}

    def get_artist(self, artist_id) -> Artist:
        artist = self.artist_map.get(artist_id)
        if artist is None:
            artist = Artist()
            self.artist_map[artist_id] = artist
        return artist

    def get_album(self, id) -> Album:
        item = self.album_map.get(id)
        if item is None:
            item = Album()
            self.album_map[id] = item
        return item

    def get_track(self, id) -> Track:
        item = self.track_map.get(id)
        if item is None:
            item = Track()
            self.track_map[id] = item
        return item

    def load_entity(self, entity : Entity, data: dict[str, Any]):
        entity.id = data.get("id")
        entity.mbid = data.get("mbid")

        entity.title = data.get("title")
        entity.duration_ms = data.get("duration_ms")

        entity.fav = data.get("fav")
        entity.rating = data.get("rating")
        entity.play_count = data.get("play_count")
        entity.date_added = _str_to_dt(data.get("date_added"))
        entity.date_released = _str_to_dt(data.get("date_released"))
        entity.date_last_play = _str_to_dt(data.get("date_last_play"))
        entity.auto_score = data.get("auto_score")

        entity.local_id = data.get("local_id")
        entity.local_path = data.get("local_path")
        entity.resolved_percentage = data.get("resolved_percentage")


    def load_track(self, data: dict[str, Any]) -> Track:
        track = self.get_track(data.get("id"))
        self.load_entity(track, data)

        track.duration_ms = data.get("duration_ms")
        track.artists = [self.get_artist(artist_id) for artist_id in data.get("artists")]
        track.album = self.get_album(data.get("album"))
        track.has_lyrics = data.get("has_lyrics")

        self.track_map[track.id] = track
        return track

    def load_artist(self, data: dict[str, Any]):
        artist = self.get_artist(data.get("id"))
        self.load_entity(artist, data)

        artist.tracks = [self.get_track(track_id) for track_id in data.get("tracks")]
        artist.albums = [self.get_album(album_id) for album_id in data.get("albums")] if data.get("albums") else []

        self.artist_map[artist.id] = artist
        return artist

    def load_album(self, data: dict[str, Any]):
        album = self.get_album(data.get("id"))
        self.load_entity(album, data)

        album.tracks = [self.get_track(track_id) for track_id in data.get("tracks")]
        album.artists = [self.get_artist(artist_id) for artist_id in data.get("artists")]

        self.album_map[album.id] = album
        return album

    def load(self, path: Path) -> Library:
        with path.open("r", encoding="utf-8") as f:
            data = json.load(f)

        library = Library()

        library.name = data.get("name")
        library.tracks = [self.load_track(track) for track in data.get("tracks")]
        library.albums = [self.load_album(album) for album in data.get("albums")]
        library.artists = [self.load_artist(artist) for artist in data.get("artists")]

        library.top_artists = [self.artist_map[artist] for artist in data.get("top_artists")]
        library.top_tracks = [self.track_map[track] for track in data.get("top_tracks")]
        library.liked_tracks = [self.track_map[track] for track in data.get("liked_tracks")]

        library.update_cache()

        return library
