use serde::{Deserialize, Serialize};
use sqlx::types::chrono::{DateTime, Utc};
use sqlx::{Postgres, Pool};
use std::collections::HashMap;
// Note: Use tracing::info! macro directly instead of importing

/// Représente un mode de lecture pour une playlist
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PlaybackMode {
    /// Lecture séquentielle normale
    Normal,
    /// Répéter la playlist
    Repeat,
    /// Répéter un seul track
    RepeatOne,
    /// Lecture aléatoire
    Shuffle,
}

/// Représente une playlist
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Playlist {
    pub id: i64,
    pub user_id: i64,
    pub name: String,
    pub description: Option<String>,
    pub is_public: bool,
    pub playback_mode: PlaybackMode,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

/// Représente un track dans une playlist avec son ordre
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlaylistTrack {
    pub playlist_id: i64,
    pub track_id: i64,
    pub position: i32,
    pub added_at: DateTime<Utc>,
}

/// Manager pour gérer les playlists
pub struct PlaylistManager {
    pool: Pool<Postgres>,
}

impl PlaylistManager {
    pub fn new(pool: Pool<Postgres>) -> Self {
        Self { pool }
    }

    /// Créer une nouvelle playlist
    #[instrument(skip(self))]
    pub async fn create_playlist(
        &self,
        user_id: i64,
        name: &str,
        description: Option<&str>,
        is_public: bool,
    ) -> Result<Playlist, sqlx::Error> {
        let playlist = sqlx::query_as::<_, (i64, String, Option<String>, bool, DateTime<Utc>, DateTime<Utc>)>(
            "INSERT INTO playlists (user_id, name, description, is_public, created_at, updated_at) 
             VALUES ($1, $2, $3, $4, NOW(), NOW()) 
             RETURNING id, name, description, is_public, created_at, updated_at"
        )
        .bind(user_id)
        .bind(name)
        .bind(description)
        .bind(is_public)
        .fetch_one(&self.pool)
        .await?;

        let (id, name, description, is_public, created_at, updated_at) = playlist;

        tracing::info!(
            playlist_id = id,
            user_id = user_id,
            "Playlist created"
        );

        Ok(Playlist {
            id,
            user_id,
            name,
            description,
            is_public,
            playback_mode: PlaybackMode::Normal,
            created_at,
            updated_at,
        })
    }

    /// Ajouter un track à une playlist
    #[instrument(skip(self))]
    pub async fn add_track_to_playlist(
        &self,
        playlist_id: i64,
        track_id: i64,
    ) -> Result<(), sqlx::Error> {
        // Obtenir la position maximale actuelle
        let max_position: Option<i32> = sqlx::query_scalar(
            "SELECT MAX(position) FROM playlist_tracks WHERE playlist_id = $1"
        )
        .bind(playlist_id)
        .fetch_optional(&self.pool)
        .await?;

        let position = max_position.unwrap_or(0) + 1;

        sqlx::query(
            "INSERT INTO playlist_tracks (playlist_id, track_id, position, added_at) 
             VALUES ($1, $2, $3, NOW())
             ON CONFLICT (playlist_id, track_id) DO NOTHING"
        )
        .bind(playlist_id)
        .bind(track_id)
        .bind(position)
        .execute(&self.pool)
        .await?;

        tracing::info!(
            playlist_id = playlist_id,
            track_id = track_id,
            position = position,
            "Track added to playlist"
        );

        Ok(())
    }

    /// Retirer un track d'une playlist
    #[instrument(skip(self))]
    pub async fn remove_track_from_playlist(
        &self,
        playlist_id: i64,
        track_id: i64,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            "DELETE FROM playlist_tracks WHERE playlist_id = $1 AND track_id = $2"
        )
        .bind(playlist_id)
        .bind(track_id)
        .execute(&self.pool)
        .await?;

        tracing::info!(
            playlist_id = playlist_id,
            track_id = track_id,
            "Track removed from playlist"
        );

        // Réorganiser les positions pour qu'elles soient séquentielles
        self.reorder_positions(playlist_id).await?;

        Ok(())
    }

    /// Réorganiser les positions des tracks dans une playlist
    async fn reorder_positions(&self, playlist_id: i64) -> Result<(), sqlx::Error> {
        sqlx::query(
            "UPDATE playlist_tracks 
             SET position = subquery.row_num
             FROM (
                 SELECT track_id, ROW_NUMBER() OVER (ORDER BY position) AS row_num
                 FROM playlist_tracks
                 WHERE playlist_id = $1
             ) AS subquery
             WHERE playlist_tracks.track_id = subquery.track_id 
             AND playlist_tracks.playlist_id = $1"
        )
        .bind(playlist_id)
        .execute(&self.pool)
        .await?;

        Ok(())
    }

    /// Obtenir les tracks d'une playlist avec leur ordre
    #[instrument(skip(self))]
    pub async fn get_playlist_tracks(
        &self,
        playlist_id: i64,
    ) -> Result<Vec<PlaylistTrack>, sqlx::Error> {
        let tracks = sqlx::query_as::<_, (i64, i64, i32, DateTime<Utc>)>(
            "SELECT playlist_id, track_id, position, added_at 
             FROM playlist_tracks 
             WHERE playlist_id = $1 
             ORDER BY position"
        )
        .bind(playlist_id)
        .fetch_all(&self.pool)
        .await?;

        let result = tracks.into_iter().map(|(playlist_id, track_id, position, added_at)| {
            PlaylistTrack {
                playlist_id,
                track_id,
                position,
                added_at,
            }
        }).collect();

        Ok(result)
    }

    /// Mettre à jour le mode de lecture
    #[instrument(skip(self))]
    pub async fn set_playback_mode(
        &self,
        playlist_id: i64,
        mode: PlaybackMode,
    ) -> Result<(), sqlx::Error> {
        let mode_str = match mode {
            PlaybackMode::Normal => "normal",
            PlaybackMode::Repeat => "repeat",
            PlaybackMode::RepeatOne => "repeat_one",
            PlaybackMode::Shuffle => "shuffle",
        };

        sqlx::query(
            "UPDATE playlists SET playback_mode = $1, updated_at = NOW() WHERE id = $2"
        )
        .bind(mode_str)
        .bind(playlist_id)
        .execute(&self.pool)
        .await?;

        tracing::info!(playlist_id = playlist_id, mode = ?mode, "Playback mode updated");

        Ok(())
    }

    /// Obtenir les playlists d'un user
    #[instrument(skip(self))]
    pub async fn get_user_playlists(&self, user_id: i64) -> Result<Vec<Playlist>, sqlx::Error> {
        let playlists = sqlx::query_as::<_, (i64, i64, String, Option<String>, bool, DateTime<Utc>, DateTime<Utc>)>(
            "SELECT id, user_id, name, description, is_public, created_at, updated_at 
             FROM playlists 
             WHERE user_id = $1 
             ORDER BY updated_at DESC"
        )
        .bind(user_id)
        .fetch_all(&self.pool)
        .await?;

        let result = playlists.into_iter().map(|(id, user_id, name, description, is_public, created_at, updated_at)| {
            Playlist {
                id,
                user_id,
                name,
                description,
                is_public,
                playback_mode: PlaybackMode::Normal,
                created_at,
                updated_at,
            }
        }).collect();

        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_playlist_manager() {
        // Note: Ces tests nécessitent une base de données de test
        assert!(true);
    }
}
