import random
from dataclasses import dataclass

from gps import Coordinates
from song import Song

USER_SCORE_WEIGHT = 0.7
ARTIST_WEIGHT = 0.1
TAG_WEIGHT = 0.1
RANDOM_WEIGHT = 0.1
RECENT_PENALTY = 0.5
RECENT_COUNT = 10
QUEUE_SIZE = 3


type UserScoredSong = tuple[Song, int]


@dataclass
class Rank:
    user_score: float
    tag: float
    artist: float
    random: float
    recent_penalty: float

    def total(self) -> float:
        return self.user_score + self.tag + self.artist + self.random - self.recent_penalty


@dataclass
class Room:
    id: int
    coord: Coordinates
    name: str
    pin: int | None
    tags: set[str]
    range_size: int  # in meters ??

    songs: dict[str, UserScoredSong]  # all songs + user score (the playlist)
    history: list[Song]  # all songs previously played

    ## playing queue info
    playing: list[Song]
    playing_idx: int

    def renew_queue(self):
        for song in self.playing:
            self.history.append(song)
        self.playing.clear()
        self.playing_idx = 0

        rankings: dict[str, float] = {}
        for id, (song, user_score) in self.songs.items():
            rankings[id] = self.rank_song(song, user_score).total()

        # sort dict items by their values and pick the highest 3
        top_items = sorted(rankings.items(), key=lambda item: item[1], reverse=True)[:QUEUE_SIZE]  # [:3]
        self.playing = list(map(lambda x: self.songs[x[0]][0], top_items))  # remove the ranking and take only the songs

    def rank_song_from_id(self, id: str) -> Rank:
        scored = self.songs[id]
        return self.rank_song(scored[0], scored[1])

    def rank_song(self, song: Song, user_score: int) -> Rank:
        song_items = self.songs.items()

        lowest_score = min(song_items, key=lambda item: item[1][1])[1][1]
        highest_score = max(song_items, key=lambda item: item[1][1])[1][1]

        score_rank = translate(user_score, lowest_score, highest_score, 0.0, USER_SCORE_WEIGHT)

        recent_songs = self.history[-RECENT_COUNT:]

        tag_counts = {}
        artist_counts = {}
        for recent_song in recent_songs:
            for tag in recent_song.tags:
                if tag not in tag_counts:
                    tag_counts[tag] = 0
                tag_counts[tag] += 1
            if recent_song.artist not in artist_counts:
                artist_counts[recent_song.artist] = 0
            artist_counts[recent_song.artist] += 1

        tag_total = 0
        for tag in song.tags:
            if tag in tag_counts:
                tag_total += tag_counts[tag]

        artist_total = artist_counts[song.artist] if song.artist in artist_counts else 0

        tag_value = min(RECENT_COUNT, len(self.history)) - tag_total
        artist_value = min(RECENT_COUNT, len(self.history)) - artist_total

        tag_rank = translate(tag_value, 0, min(RECENT_COUNT, len(self.history)), 0, TAG_WEIGHT)
        artist_rank = translate(artist_value, 0, min(RECENT_COUNT, len(self.history)), 0, ARTIST_WEIGHT)

        random_rank = translate(random.random(), 0, 1, 0.0, RANDOM_WEIGHT)

        recent_penalty = RECENT_PENALTY if song in recent_songs else 0

        return Rank(score_rank, tag_rank, artist_rank, random_rank, recent_penalty)


def translate(value: float, in_min: float, in_max: float, out_min: float, out_max: float):
    if in_max == in_min:
        return out_min
    return (value - in_min) / (in_max - in_min) * (out_max - out_min) + out_min


def test_algo():
    songs = [
        Song("paulham", "Io e i miei banchi", "Paul Ham", ["pop"], "", ""),
        Song("cisco", "CiscoPT", "Cantarex", ["rap"], "", ""),
        Song("vpn", "VPN", "Cantarex", ["rap"], "", ""),
        Song("gang", "Gang Gang Gang", "Cantarex", ["rap"], "", ""),
        Song("bertha1", "Rindondantha", "Berthanetti", ["rock"], "", ""),
        Song("bertha2", "Ragatthi", "Berthanetti", ["rock"], "", ""),
        Song("bertha3", "Tranthathione", "Berthanetti", ["rock"], "", ""),
        Song("cexx", "Spritz", "Cex", ["kpop"], "", ""),
    ]

    room = Room(
        123,
        (0.0, 0.0),
        "test",
        None,
        set(["rock", "rap"]),
        100,
        {
            "paulham": (songs[0], 7),
            "cisco": (songs[1], 5),
            "vpn": (songs[2], 11),
            "gang": (songs[3], 10),
            "bertha1": (songs[4], 4),
            "bertha2": (songs[5], 5),
            "bertha3": (songs[6], -4),
            "cexx": (songs[7], 12),
        },
        [],
        [songs[2], songs[0], songs[1]],
        1,
    )
    room.renew_queue()
    print(room.playing)
