from src.Library import *
from src.helpers import *

from pathlib import Path
import json


def is_resolved(track: Track):
    return track.local_path is not None


def get_mapping(mapping_path) -> dict[str, Any]:
    data = {}

    with Path(mapping_path).open("r", encoding="utf-8") as f:
        data = json.load(f)

    return data["track_map"]


class AlbumStat:
    def __init__(self):
        self.album: Album = Album()
        self.resolved_precent: float = 0

    def calc_resolved_precent(self):
        self.resolved_precent = (sum(1 for track in self.album.tracks if is_resolved(track)) / len(
            self.album.tracks)) * 100

    def get_str(self):
        return (f"{str(int(self.resolved_precent))} % - "
                f"{self.album.title} - "
                f"{str(self.album.artists[0].title) if len(self.album.artists) else " "}")


class ArtistStat:
    def __init__(self):
        self.artist: Artist = Artist()
        self.resolved_precent: float = 0
        self.albums_stats: list[AlbumStat] = []

    def calc_resolved_precent(self):
        if len(self.artist.tracks) == 0:
            self.resolved_precent = 0
            return

        self.resolved_precent = (sum(1 for track in self.artist.tracks if is_resolved(track)) / len(
            self.artist.tracks)) * 100

    def get_str(self):
        return (f"{str(int(self.resolved_precent))} % - "
                f"{self.artist.title}")

class LibraryStats:
    def __init__(self, mapping_path : Path, local_lib: Library, remote_lib: Library):
        self.max_popular_albums = 100
        self.max_popular_artists = 100

        self.max_unresolved_tracks = 100
        self.max_unresolved_albums = 20
        self.resolved_percentage_threshold = 30

        self.albums: List[AlbumStat] = []
        self.artists: List[ArtistStat] = []

        self.albums_map: dict[str, AlbumStat] = {}
        self.artists_map: dict[str, ArtistStat] = {}

        self.local_lib: Library = local_lib
        self.remote_lib: Library = remote_lib

        self.mapping: dict[str, Any] = get_mapping(mapping_path)

        self.remote_id_map: dict[str, Track] = {}
        self.local_id_map: dict[str, Track] = {}

        self.find_add_local_paths()
        self.score_artists()
        self.score_albums()

    def find_add_local_paths(self):
        self.remote_id_map = {track.id: track for track in self.remote_lib.tracks}
        self.local_id_map = {track.id: track for track in self.local_lib.tracks}

        for remote, local in self.mapping.items():
            self.remote_id_map[remote].local_path = self.local_id_map[local].local_path

    def score_tracks(self):
        for idx, track in enumerate(self.remote_lib.top_tracks):
            score = len(self.remote_lib.top_tracks) - idx
            track.auto_score = score

    def score_artists(self):
        for artist in self.remote_lib.artists:
            artist_stat = ArtistStat()
            artist_stat.artist = artist
            artist_stat.artist.auto_score = 0
            self.artists_map[artist_stat.artist.id] = artist_stat
            self.artists.append(artist_stat)

        for track in self.remote_lib.tracks:
            for artist in track.artists:
                self.artists_map[artist.id].artist.auto_score += track.auto_score

        for artist_stat in self.artists:
            artist_stat.calc_resolved_precent()

        self.artists = sorted(self.artists, key=lambda item: item.score, reverse=True)

    def score_albums(self):
        for album in self.remote_lib.albums:
            stat = AlbumStat()
            stat.album = album
            self.albums_map[album.id] = stat

        for track in self.remote_lib.tracks:
            self.albums_map[track.album.id].album.auto_score += track.auto_score

        for _, album in self.albums_map.items():
            album.calc_resolved_precent()

        self.albums = [item[1] for item in
                       sorted(self.albums_map.items(), key=lambda item: item[1].score, reverse=True)]

        for album_stat in self.albums:
            for artist in album_stat.album.artists:
                self.artists_map[artist.id].albums_stats.append(album_stat)


    def gen_album(self, album_stat, condition):
        tracks = []

        for track in album_stat.album.tracks:
            if condition.should_add_track(track):
                tracks.append(f"{"✔" if is_resolved(track) else "𐄂"}  {track.title} -> {track.local_path}")

        return {
            "album" : album_stat.get_str(),
            "tracks" : tracks
        }

    def gen_artist(self, artist_stat, condition):
        albums = []

        for album_stat in artist_stat.albums_stats:
            if condition.should_add_album(album_stat):
                albums.append(self.gen_album(album_stat, condition))

        return {
            "artist" : artist_stat.get_str(),
            "albums" : albums,
        }

    def generate_stat_tree(self, condition):
        artists = []

        for artist_stat in self.artists:
            if condition.should_add_artist(artist_stat):
                artists.append(self.gen_artist(artist_stat, condition))

        return {
            "artists" : artists
        }

    def save_tree(self, output_path, condition):
        save_data(self.generate_stat_tree(condition), output_path)

    def save_all(self, output_path):

        class Condition:
            def should_add_artist(self, item):
                return True

            def should_add_album(self, item):
                return True

            def should_add_track(self, item):
                return True


        self.save_tree(output_path, Condition())


    def save_threshold(self, output_path, threshold = 80):

        class Condition:
            def should_add_artist(self, item : ArtistStat):
                for album in item.albums_stats:
                    if self.should_add_album(album):
                        return True

                return False

            def should_add_album(self, item: AlbumStat):
                return item.resolved_precent < threshold and len(item.album.tracks) > 3

            def should_add_track(self, item: Track):
                return not is_resolved(item)

        self.save_tree(output_path, Condition())



def log_stats(mapping_path: Path, output_dir: Path, local_lib: Library, remote_lib: Library):
    set_workdir(output_dir)

    stat = LibraryStats(mapping_path, local_lib, remote_lib)

    stat.save_all("stats_all")
    stat.save_threshold("stats_missing")
