//! Playlist type definition
//! 
//! This module defines the Playlist type and related structures.

use serde::{Deserialize, Serialize};
use crate::types::{TrackId, UserId};
use chrono::{DateTime, Utc};

/// Playlist identifier
pub type PlaylistId = uuid::Uuid;

/// Playlist information
/// 
/// Represents a playlist in the Veza system.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Playlist {
    /// Playlist ID
    pub id: PlaylistId,
    /// Playlist name
    pub name: String,
    /// Optional playlist description
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// User ID of the playlist owner
    pub owner_id: UserId,
    /// List of track IDs in the playlist
    pub track_ids: Vec<TrackId>,
    /// Whether the playlist is public
    pub is_public: bool,
    /// Playlist creation timestamp
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Utc>>,
    /// Playlist last update timestamp
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Utc>>,
}

impl Playlist {
    /// Create a new Playlist instance
    pub fn new(id: PlaylistId, name: String, owner_id: UserId) -> Self {
        Self {
            id,
            name,
            description: None,
            owner_id,
            track_ids: Vec::new(),
            is_public: false,
            created_at: None,
            updated_at: None,
        }
    }

    /// Add a track to the playlist
    pub fn add_track(&mut self, track_id: TrackId) {
        if !self.track_ids.contains(&track_id) {
            self.track_ids.push(track_id);
        }
    }

    /// Remove a track from the playlist
    pub fn remove_track(&mut self, track_id: TrackId) {
        self.track_ids.retain(|&id| id != track_id);
    }

    /// Get the number of tracks in the playlist
    pub fn track_count(&self) -> usize {
        self.track_ids.len()
    }

    /// Check if the playlist is empty
    pub fn is_empty(&self) -> bool {
        self.track_ids.is_empty()
    }

    /// Validate playlist data
    pub fn validate(&self) -> Result<(), String> {
        if self.name.is_empty() {
            return Err("Playlist name cannot be empty".to_string());
        }
        if self.name.len() > 200 {
            return Err("Playlist name cannot exceed 200 characters".to_string());
        }
        Ok(())
    }
}

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

    #[test]
    fn test_playlist_new() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        assert_eq!(playlist.id, playlist_id);
        assert_eq!(playlist.name, "My Playlist");
        assert_eq!(playlist.owner_id, owner_id);
        assert!(playlist.track_ids.is_empty());
        assert!(!playlist.is_public);
        assert!(playlist.description.is_none());
    }

    #[test]
    fn test_playlist_serialize() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        let json = serde_json::to_string(&playlist).unwrap();
        assert!(json.contains("\"name\":\"My Playlist\""));
        assert!(json.contains("\"is_public\":false"));
        assert!(json.contains("\"track_ids\":[]"));
    }

    #[test]
    fn test_playlist_deserialize() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let json = format!(
            r#"{{"id":"{}","name":"My Playlist","owner_id":"{}","track_ids":[],"is_public":false}}"#,
            playlist_id, owner_id
        );
        let playlist: Playlist = serde_json::from_str(&json).unwrap();
        assert_eq!(playlist.id, playlist_id);
        assert_eq!(playlist.name, "My Playlist");
        assert_eq!(playlist.owner_id, owner_id);
        assert!(playlist.track_ids.is_empty());
    }

    #[test]
    fn test_playlist_add_track() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let track_id = Uuid::new_v4();
        let mut playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        
        playlist.add_track(track_id);
        assert_eq!(playlist.track_count(), 1);
        assert!(playlist.track_ids.contains(&track_id));
    }

    #[test]
    fn test_playlist_add_duplicate_track() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let track_id = Uuid::new_v4();
        let mut playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        
        playlist.add_track(track_id);
        playlist.add_track(track_id);
        assert_eq!(playlist.track_count(), 1);
    }

    #[test]
    fn test_playlist_remove_track() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let track_id = Uuid::new_v4();
        let mut playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        
        playlist.add_track(track_id);
        assert_eq!(playlist.track_count(), 1);
        
        playlist.remove_track(track_id);
        assert_eq!(playlist.track_count(), 0);
        assert!(playlist.is_empty());
    }

    #[test]
    fn test_playlist_is_empty() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        assert!(playlist.is_empty());
    }

    #[test]
    fn test_playlist_validate_success() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
        assert!(playlist.validate().is_ok());
    }

    #[test]
    fn test_playlist_validate_empty_name() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let playlist = Playlist::new(playlist_id, "".to_string(), owner_id);
        assert!(playlist.validate().is_err());
    }

    #[test]
    fn test_playlist_validate_name_too_long() {
        let playlist_id = Uuid::new_v4();
        let owner_id = Uuid::new_v4();
        let long_name = "a".repeat(201);
        let playlist = Playlist::new(playlist_id, long_name, owner_id);
        assert!(playlist.validate().is_err());
    }
}

