//! Module d'accès au catalogue Qobuz (albums, tracks, artistes, playlists)

use super::QobuzApi;
use crate::error::Result;
use crate::models::*;
use serde::Deserialize;
use tracing::debug;

/// Réponse paginée de l'API
#[derive(Debug, Deserialize)]
struct PaginatedResponse<T> {
    items: Vec<T>,
    #[serde(default)]
    total: Option<u32>,
    #[serde(default)]
    limit: Option<u32>,
    #[serde(default)]
    offset: Option<u32>,
}

/// Réponse de l'endpoint /album/get
#[derive(Debug, Deserialize)]
pub(crate) struct AlbumResponse {
    id: String,
    title: String,
    artist: ArtistResponse,
    #[serde(default)]
    tracks_count: Option<u32>,
    #[serde(default)]
    duration: Option<u32>,
    #[serde(default)]
    release_date_original: Option<String>,
    #[serde(default)]
    image: Option<ImageResponse>,
    #[serde(default = "default_streamable")]
    streamable: bool,
    #[serde(default)]
    description: Option<String>,
    #[serde(default)]
    maximum_sampling_rate: Option<f64>,
    #[serde(default)]
    maximum_bit_depth: Option<u32>,
    #[serde(default)]
    genre: Option<GenreResponse>,
    #[serde(default)]
    label: Option<LabelResponse>,
    #[serde(default)]
    tracks: Option<PaginatedResponse<TrackResponse>>,
}

/// Réponse de l'endpoint /track/get
#[derive(Debug, Deserialize)]
pub(crate) struct TrackResponse {
    id: String,
    title: String,
    #[serde(default)]
    performer: Option<ArtistResponse>,
    #[serde(default)]
    artist: Option<ArtistResponse>,
    #[serde(default)]
    album: Option<AlbumResponse>,
    duration: u32,
    track_number: u32,
    media_number: u32,
    #[serde(default = "default_streamable")]
    streamable: bool,
}

/// Réponse artiste
#[derive(Debug, Deserialize)]
pub(crate) struct ArtistResponse {
    id: u64,
    name: String,
    #[serde(default)]
    image: Option<ImageResponse>,
    #[serde(default)]
    albums: Option<PaginatedResponse<AlbumResponse>>,
}

/// Réponse image
#[derive(Debug, Deserialize)]
struct ImageResponse {
    #[serde(default)]
    large: Option<String>,
}

/// Réponse genre
#[derive(Debug, Deserialize)]
struct GenreResponse {
    #[serde(default)]
    id: Option<u32>,
    name: String,
}

/// Réponse label
#[derive(Debug, Deserialize)]
struct LabelResponse {
    name: String,
}

/// Réponse playlist
#[derive(Debug, Deserialize)]
pub(crate) struct PlaylistResponse {
    id: u64,
    name: String,
    #[serde(default)]
    description: Option<String>,
    #[serde(default)]
    tracks_count: Option<u32>,
    #[serde(default)]
    duration: Option<u32>,
    #[serde(default)]
    images300: Option<Vec<String>>,
    #[serde(default)]
    is_public: bool,
    #[serde(default)]
    owner: Option<OwnerResponse>,
    #[serde(default)]
    tracks: Option<PaginatedResponse<TrackResponse>>,
}

/// Réponse propriétaire
#[derive(Debug, Deserialize)]
struct OwnerResponse {
    id: u64,
    name: String,
}

/// Réponse genres list
#[derive(Debug, Deserialize)]
struct GenresResponse {
    genres: PaginatedResponse<GenreResponse>,
}

/// Réponse albums featured
#[derive(Debug, Deserialize)]
struct FeaturedAlbumsResponse {
    albums: PaginatedResponse<AlbumResponse>,
}

/// Réponse playlists featured
#[derive(Debug, Deserialize)]
struct FeaturedPlaylistsResponse {
    playlists: PaginatedResponse<PlaylistResponse>,
}

/// Réponse search
#[derive(Debug, Deserialize)]
struct SearchResponse {
    #[serde(default)]
    albums: Option<PaginatedResponse<AlbumResponse>>,
    #[serde(default)]
    artists: Option<PaginatedResponse<ArtistResponse>>,
    #[serde(default)]
    tracks: Option<PaginatedResponse<TrackResponse>>,
    #[serde(default)]
    playlists: Option<PaginatedResponse<PlaylistResponse>>,
}

/// Réponse track file URL
#[derive(Debug, Deserialize)]
struct FileUrlResponse {
    url: String,
    mime_type: String,
    sampling_rate: u32,
    bit_depth: u32,
    format_id: u8,
}

fn default_streamable() -> bool {
    true
}

impl QobuzApi {
    /// Récupère les détails d'un album
    pub async fn get_album(&self, album_id: &str) -> Result<Album> {
        debug!("Fetching album {}", album_id);
        let params = [("album_id", album_id)];
        let response: AlbumResponse = self.get("/album/get", &params).await?;
        Ok(Self::parse_album(response))
    }

    /// Récupère les tracks d'un album
    pub async fn get_album_tracks(&self, album_id: &str) -> Result<Vec<Track>> {
        debug!("Fetching tracks for album {}", album_id);
        let params = [("album_id", album_id)];
        let mut response: AlbumResponse = self.get("/album/get", &params).await?;

        if let Some(tracks) = response.tracks.take() {
            let album = Self::parse_album(response);
            Ok(tracks
                .items
                .into_iter()
                .map(|t| Self::parse_track(t, Some(album.clone())))
                .collect())
        } else {
            Ok(Vec::new())
        }
    }

    /// Récupère les détails d'une track
    pub async fn get_track(&self, track_id: &str) -> Result<Track> {
        debug!("Fetching track {}", track_id);
        let params = [("track_id", track_id)];
        let response: TrackResponse = self.get("/track/get", &params).await?;
        Ok(Self::parse_track(response, None))
    }

    /// Récupère l'URL de streaming d'une track
    pub async fn get_file_url(&self, track_id: &str) -> Result<StreamInfo> {
        debug!("Fetching file URL for track {}", track_id);
        let format_id = self.format_id.id().to_string();
        let params = [
            ("track_id", track_id),
            ("format_id", &format_id),
            ("intent", "stream"),
        ];
        let response: FileUrlResponse = self.get("/track/getFileUrl", &params).await?;

        Ok(StreamInfo {
            url: response.url,
            mime_type: response.mime_type,
            sampling_rate: response.sampling_rate,
            bit_depth: response.bit_depth,
            format_id: response.format_id,
            expires_at: chrono::Utc::now() + chrono::Duration::minutes(5),
        })
    }

    /// Récupère les albums d'un artiste
    pub async fn get_artist_albums(&self, artist_id: &str) -> Result<Vec<Album>> {
        debug!("Fetching albums for artist {}", artist_id);
        let params = [("artist_id", artist_id), ("extra", "albums")];
        let response: ArtistResponse = self.get("/artist/get", &params).await?;

        if let Some(albums) = response.albums {
            Ok(albums
                .items
                .into_iter()
                .map(Self::parse_album)
                .filter(|a| a.streamable)
                .collect())
        } else {
            Ok(Vec::new())
        }
    }

    /// Récupère les artistes similaires
    pub async fn get_similar_artists(&self, artist_id: &str) -> Result<Vec<Artist>> {
        debug!("Fetching similar artists for {}", artist_id);
        let params = [("artist_id", artist_id)];

        #[derive(Debug, Deserialize)]
        struct SimilarArtistsResponse {
            artists: PaginatedResponse<ArtistResponse>,
        }

        let response: SimilarArtistsResponse =
            self.get("/artist/getSimilarArtists", &params).await?;
        Ok(response
            .artists
            .items
            .into_iter()
            .map(Self::parse_artist)
            .collect())
    }

    /// Récupère les détails d'une playlist
    pub async fn get_playlist(&self, playlist_id: &str) -> Result<Playlist> {
        debug!("Fetching playlist {}", playlist_id);
        let params = [("playlist_id", playlist_id)];
        let response: PlaylistResponse = self.get("/playlist/get", &params).await?;
        Ok(Self::parse_playlist(response))
    }

    /// Récupère les tracks d'une playlist
    pub async fn get_playlist_tracks(&self, playlist_id: &str) -> Result<Vec<Track>> {
        debug!("Fetching tracks for playlist {}", playlist_id);
        let params = [("playlist_id", playlist_id), ("extra", "tracks")];
        let response: PlaylistResponse = self.get("/playlist/get", &params).await?;

        if let Some(tracks) = response.tracks {
            Ok(tracks
                .items
                .into_iter()
                .map(|t| Self::parse_track(t, None))
                .collect())
        } else {
            Ok(Vec::new())
        }
    }

    /// Récupère la liste des genres
    pub async fn get_genres(&self) -> Result<Vec<Genre>> {
        debug!("Fetching genres");
        let response: GenresResponse = self.get("/genre/list", &[]).await?;
        Ok(response
            .genres
            .items
            .into_iter()
            .map(Self::parse_genre)
            .collect())
    }

    /// Récupère les albums featured (nouveautés, éditeur, etc.)
    pub async fn get_featured_albums(
        &self,
        genre_id: Option<&str>,
        type_: &str,
    ) -> Result<Vec<Album>> {
        debug!("Fetching featured albums (type: {})", type_);
        let mut params = vec![("type", type_), ("limit", "100")];

        if let Some(gid) = genre_id {
            params.push(("genre_ids", gid));
        }

        let response: FeaturedAlbumsResponse = self.get("/album/getFeatured", &params).await?;
        Ok(response
            .albums
            .items
            .into_iter()
            .map(Self::parse_album)
            .filter(|a| a.streamable)
            .collect())
    }

    /// Récupère les playlists featured
    pub async fn get_featured_playlists(
        &self,
        genre_id: Option<&str>,
        tags: Option<&str>,
    ) -> Result<Vec<Playlist>> {
        debug!("Fetching featured playlists");
        let mut params = vec![("type", "editor-picks"), ("limit", "100")];

        if let Some(gid) = genre_id {
            params.push(("genre_ids", gid));
        }
        if let Some(t) = tags {
            params.push(("tags", t));
        }

        let response: FeaturedPlaylistsResponse =
            self.get("/playlist/getFeatured", &params).await?;
        Ok(response
            .playlists
            .items
            .into_iter()
            .map(Self::parse_playlist)
            .collect())
    }

    /// Recherche dans le catalogue
    pub async fn search(&self, query: &str, type_: Option<&str>) -> Result<SearchResult> {
        debug!("Searching for '{}' (type: {:?})", query, type_);
        let mut params = vec![("query", query), ("limit", "200")];

        if let Some(t) = type_ {
            params.push(("type", t));
        }

        let response: SearchResponse = self.get("/catalog/search", &params).await?;

        Ok(SearchResult {
            albums: response
                .albums
                .map(|a| {
                    a.items
                        .into_iter()
                        .map(Self::parse_album)
                        .filter(|album| album.streamable)
                        .collect()
                })
                .unwrap_or_default(),
            artists: response
                .artists
                .map(|a| a.items.into_iter().map(Self::parse_artist).collect())
                .unwrap_or_default(),
            tracks: response
                .tracks
                .map(|t| {
                    t.items
                        .into_iter()
                        .map(|track| Self::parse_track(track, None))
                        .filter(|track| track.streamable)
                        .collect()
                })
                .unwrap_or_default(),
            playlists: response
                .playlists
                .map(|p| p.items.into_iter().map(Self::parse_playlist).collect())
                .unwrap_or_default(),
        })
    }

    // Fonctions de parsing publiques (utilisées aussi par le module user)

    pub(crate) fn parse_album(response: AlbumResponse) -> Album {
        Album {
            id: response.id,
            title: response.title,
            artist: Self::parse_artist(response.artist),
            tracks_count: response.tracks_count,
            duration: response.duration,
            release_date: response.release_date_original,
            image: response.image.and_then(|i| i.large),
            image_cached: None,
            streamable: response.streamable,
            description: response.description,
            maximum_sampling_rate: response.maximum_sampling_rate,
            maximum_bit_depth: response.maximum_bit_depth,
            genres: response.genre.map(|g| vec![g.name]).unwrap_or_default(),
            label: response.label.map(|l| l.name),
        }
    }

    pub(crate) fn parse_track(response: TrackResponse, album: Option<Album>) -> Track {
        let performer = response
            .performer
            .or(response.artist)
            .map(Self::parse_artist);

        let album = album.or_else(|| response.album.map(Self::parse_album));

        Track {
            id: response.id,
            title: response.title,
            performer,
            album,
            duration: response.duration,
            track_number: response.track_number,
            media_number: response.media_number,
            streamable: response.streamable,
            mime_type: None,
            sample_rate: None,
            bit_depth: None,
            channels: None,
        }
    }

    pub(crate) fn parse_artist(response: ArtistResponse) -> Artist {
        Artist {
            id: response.id.to_string(),
            name: response.name,
            image: response.image.and_then(|i| i.large),
            image_cached: None,
        }
    }

    pub(crate) fn parse_playlist(response: PlaylistResponse) -> Playlist {
        Playlist {
            id: response.id.to_string(),
            name: response.name,
            description: response.description,
            tracks_count: response.tracks_count,
            duration: response.duration,
            image: response.images300.and_then(|imgs| imgs.first().cloned()),
            image_cached: None,
            is_public: response.is_public,
            owner: response.owner.map(|o| PlaylistOwner {
                id: o.id,
                name: o.name,
            }),
        }
    }

    pub(crate) fn parse_genre(response: GenreResponse) -> Genre {
        Genre {
            id: response.id,
            name: response.name,
            children: Vec::new(),
        }
    }
}
