from collections.abc import Iterable

from fastapi import HTTPException
from folkugat_web.model import playlists as playlists_model
from folkugat_web.model import temes as model
from folkugat_web.model.lilypond import score as lilypond_model
from folkugat_web.services import playlists as playlists_service
from folkugat_web.services.temes import lyrics as lyrics_service
from folkugat_web.services.temes import properties as properties_service
from folkugat_web.services.temes import query as temes_q
from folkugat_web.services.temes import scores as scores_service
from folkugat_web.utils import FnChain

UNKNOWN_TITLE = "Desconegut"


def unknown_tune() -> lilypond_model.LilypondTune:
    return lilypond_model.LilypondTune(
        header=lilypond_model.HeaderData(
            title=UNKNOWN_TITLE,
            composer=None,
        ),
        score_source=None,
        lyrics=None,
        is_unknown=True,
    )


def tune_from_tema(
    tema: model.Tema | None,
    score_source: str | None = None,
    score_id: int | None = None,
) -> lilypond_model.LilypondTune:
    """
    The given `tema` is assumed to have properties, lyrics and scores (if source is None)
    """
    if tema is None:
        return unknown_tune()

    # Determine the score source to use
    effective_source = score_source
    if effective_source is None and tema.scores:
        if score_id is not None:
            # Find the specific score by ID
            for score in tema.scores:
                if score.id == score_id:
                    effective_source = score.source
                    break
        if effective_source is None:
            # Fallback to main_score or first score
            main = tema.main_score()
            effective_source = main.source if main else tema.scores[0].source

    return lilypond_model.LilypondTune(
        header=lilypond_model.HeaderData.from_tema(tema=tema),
        score_source=effective_source,
        lyrics=lilypond_model.LyricsText.from_lyrics(lyrics=tema.lyrics[0]) if tema.lyrics else None,
    )


def tunes_from_temes(temes: Iterable[model.Tema | None]) -> list[lilypond_model.LilypondTune]:
    """
    All `Tema` in `temes` are assumed to have properties, lyrics and scores
    """
    return [tune_from_tema(tema) for tema in temes]


def tune_from_tema_id(tema_id: int, score_source: str | None = None) -> lilypond_model.LilypondTune:
    tema = temes_q.get_tema_by_id(tema_id)
    if not tema:
        raise HTTPException(status_code=404, detail="Could not find tema!")
    tema = (
        FnChain.transform(tema) |
        properties_service.add_properties_to_tema |
        lyrics_service.add_lyrics_to_tema
    ).result()
    if score_source is None:
        tema = scores_service.add_scores_to_tema(tema)
    return tune_from_tema(tema=tema, score_source=score_source)


def tunes_from_tema_ids(tema_ids: list[int]) -> list[lilypond_model.LilypondTune]:
    temes = (
        FnChain.transform(temes_q.get_temes_by_ids(tema_ids=tema_ids)) |
        properties_service.add_properties_to_temes |
        lyrics_service.add_lyrics_to_temes |
        scores_service.add_scores_to_temes |
        list
    ).result()
    return tunes_from_temes(temes=temes)


def build_set_title(temes: list[model.Tema | None]) -> str:
    def build_title(tema: model.Tema | None) -> str:
        return tema.title if tema else UNKNOWN_TITLE
    return " i ".join(filter(bool, [
        ", ".join([build_title(tema) for tema in temes[:-1]]),
        build_title(temes[-1])
    ]))


def set_from_set(set_entry: playlists_model.Set) -> lilypond_model.LilypondSet:
    """
    The tune_set is assumed to be enriched with tunes
    """
    temes_by_id = {
        tema_in_set.tema_id: tema_in_set.tema
        for tema_in_set in set_entry.temes
        if tema_in_set.id is not None and tema_in_set.tema
    }
    temes = [temes_by_id.get(t.tema_id) for t in set_entry.temes]
    set_title = build_set_title(temes=temes)

    # Build tunes with score_id consideration
    tunes = [
        tune_from_tema(
            tema=temes_by_id.get(tema_in_set.tema_id),
            score_id=tema_in_set.score_id,
        )
        for tema_in_set in set_entry.temes
    ]

    return lilypond_model.LilypondSet(
        title=set_title,
        tunes=tunes
    )


def playlist_from_playlist(playlist: playlists_model.Playlist) -> lilypond_model.LilypondPlaylist:
    """
    The playlist is assumed to be enriched with tunes
    """
    lilypond_sets = []
    for set_entry in playlist.sets:
        lilypond_set = set_from_set(set_entry)
        if lilypond_set.tunes and all(map(playlists_service._elegible_for_set_score, lilypond_set.tunes)):
            lilypond_sets.append(lilypond_set)
    
    playlist_title = playlist.name or "Llista"
    return lilypond_model.LilypondPlaylist(
        title=playlist_title,
        sets=lilypond_sets
    )
