use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::BTreeMap;
use uuid::Uuid;

// =============================================================================
// CUSTOM DESERIALIZERS
// =============================================================================

/// Deserialize a UUID, treating -1 as None (for ghost/deleted users)
fn deserialize_uuid_or_negative_one<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::de::Error;
    use serde_json::Value;

    let value = Value::deserialize(deserializer)?;

    match value {
        Value::String(s) => Uuid::parse_str(&s).map_err(D::Error::custom),
        Value::Number(n) => {
            // Handle -1 as a special case (deleted/ghost user)
            if n.as_i64() == Some(-1) {
                // Return nil UUID as a placeholder for invalid users
                Ok(Uuid::nil())
            } else {
                Err(D::Error::custom(format!(
                    "Expected UUID string or -1, got number: {}",
                    n
                )))
            }
        }
        _ => Err(D::Error::custom(format!(
            "Expected UUID string or -1, got: {}",
            value.get_type()
        ))),
    }
}

trait JsonValueExt {
    fn get_type(&self) -> &'static str;
}

impl JsonValueExt for serde_json::Value {
    fn get_type(&self) -> &'static str {
        match self {
            serde_json::Value::Null => "null",
            serde_json::Value::Bool(_) => "bool",
            serde_json::Value::Number(_) => "number",
            serde_json::Value::String(_) => "string",
            serde_json::Value::Array(_) => "array",
            serde_json::Value::Object(_) => "object",
        }
    }
}

/// Request body for the /api/discover endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscoverRequest {
    /// For different products: "archive" or null for homepage
    #[serde(skip_serializing_if = "Option::is_none")]
    pub product: Option<String>,

    /// Language code (default: "en")
    pub language: String,

    /// Start index (0-based)
    pub start_index: i32,

    /// Number of sections to return
    pub page_size: i32,

    /// Optional: Specific section name to fetch
    #[serde(skip_serializing_if = "Option::is_none")]
    pub section_name: Option<String>,

    /// Optional: Specific section content type
    #[serde(skip_serializing_if = "Option::is_none")]
    pub section_content: Option<String>,

    /// Optional: Secondary section content
    #[serde(skip_serializing_if = "Option::is_none")]
    pub secondary_section_content: Option<String>,

    /// Page number for infinite scroll
    pub page: i32,

    /// Platform: "mobile" or "web"
    pub platform: String,

    /// Whether to disable shuffle
    #[serde(skip_serializing_if = "Option::is_none")]
    pub disable_shuffle: Option<bool>,

    /// Optional: How many elements to return per section
    #[serde(skip_serializing_if = "Option::is_none")]
    pub section_size: Option<i32>,
}

impl Default for DiscoverRequest {
    fn default() -> Self {
        Self {
            product: None,
            language: "en".to_string(),
            start_index: 0,
            page_size: 3,
            section_name: None,
            section_content: None,
            secondary_section_content: None,
            page: 0,
            platform: "web".to_string(),
            disable_shuffle: None,
            section_size: None,
        }
    }
}

/// Response from /api/discover endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DiscoverResponse {
    pub sections: Vec<DiscoverSection>,
    pub page: i32,
    pub total_sections: i32,
    pub page_size: i32,
    pub start_index: i32,
}

/// Generic discover section - sections are polymorphic with different types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "section_type", deny_unknown_fields)]
pub enum DiscoverSection {
    #[serde(rename = "playlist")]
    Playlist(PlaylistSection),

    #[serde(rename = "playlist_list")]
    PlaylistList(PlaylistListSection),

    #[serde(rename = "user_list")]
    UserList(UserListSection),

    #[serde(rename = "featured_feed")]
    FeaturedFeed(FeaturedFeedSection),

    #[serde(rename = "shortcut")]
    Shortcut(ShortcutSection),

    #[serde(rename = "promo")]
    Promo(PromoSection),

    #[serde(rename = "banner")]
    Banner(BannerSection),

    #[serde(rename = "hooks")]
    Hooks(HooksSection),

    #[serde(rename = "contest_list")]
    ContestList(ContestListSection),

    #[serde(rename = "persona_list")]
    PersonaList(PersonaListSection),

    #[serde(rename = "style_list")]
    StyleList(StyleListSection),
}

/// Base fields for all sections
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BaseSectionFields {
    pub title: String,
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub link: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub mobile_link: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub options: Option<Vec<String>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub selected_option: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub secondary_options: Option<Vec<String>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub secondary_selected_option: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub section_name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub style_type: Option<String>,
}

/// Playlist section with clips
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PlaylistSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Vec<DiscoverClip>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub preview_items_count: Option<i32>,
}

/// Playlist list section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PlaylistListSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<DiscoverPlaylistItem>,
}

/// Discover playlist item
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DiscoverPlaylistItem {
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    pub name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub cover_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub playlist_clips_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Utc>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Utc>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub upvote_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub play_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_liked: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_by_id: Option<Uuid>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_by_handle: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub aura_settings: Option<BTreeMap<String, serde_json::Value>>,
}

/// Simplified clip for discover sections
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DiscoverClip {
    pub id: Uuid,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    pub title: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_large_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub video_url: Option<String>,

    pub user_id: Uuid,

    pub handle: String,

    pub display_name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_image_url: Option<String>,

    pub play_count: i32,

    pub upvote_count: i32,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub comment_count: Option<i32>,

    pub created_at: DateTime<Utc>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_liked: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_public: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_trashed: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<BTreeMap<String, serde_json::Value>>,
}

/// User list section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UserListSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<DiscoverUserItem>,
}

/// Simplified user item for discover
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DiscoverUserItem {
    #[serde(deserialize_with = "deserialize_uuid_or_negative_one")]
    pub user_id: Uuid,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    pub handle: String,

    pub display_name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub bio: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub follower_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_followed: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_self: Option<bool>,
}

/// Featured feed section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FeaturedFeedSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<BTreeMap<String, serde_json::Value>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub item_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub feed_metadata: Option<BTreeMap<String, serde_json::Value>>,
}

/// Shortcut section with navigation items
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ShortcutSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<ShortcutItem>,
}

/// Individual shortcut item
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ShortcutItem {
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    pub name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub redirect_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub shortcut_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub shortcut_context: Option<BTreeMap<String, serde_json::Value>>,
}

/// Promo section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromoSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<PromoItem>,
}

/// Promo item
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromoItem {
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub primary_headline: Option<PromoHeadline>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub secondary_headline: Option<PromoHeadline>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub primary_cta: Option<PromoCta>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub secondary_cta: Option<PromoCta>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub background_image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub card_height: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub badge: Option<PromoBadge>,
}

/// Promo headline
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromoHeadline {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub color: Option<String>,
}

/// Promo call-to-action
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromoCta {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub action: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub label: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub text_color: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub background_color: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
}

/// Promo badge
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromoBadge {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub label: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub text_color: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub background_color: Option<String>,
}

/// Banner section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BannerSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Vec<BannerItem>>,
}

/// Banner item
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BannerItem {
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub redirect_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub countries: Option<Vec<String>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub visible: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub tag: Option<serde_json::Value>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub title_text: Option<serde_json::Value>,
}

/// Hooks section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HooksSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<BTreeMap<String, serde_json::Value>>,
}

/// Contest list section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ContestListSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<BTreeMap<String, serde_json::Value>>,
}

/// Persona list section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PersonaListSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<BTreeMap<String, serde_json::Value>>,
}

/// Style list section
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StyleListSection {
    #[serde(flatten)]
    pub base: BaseSectionFields,

    pub items: Vec<BTreeMap<String, serde_json::Value>>,
}

// =============================================================================
// SHORTCUTS RESPONSE
// =============================================================================

/// Response from /api/discover/shortcuts_songs endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ShortcutResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shortcut_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub playlist: Option<ShortcutPlaylist>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub page: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub total_sections: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub page_size: Option<i32>,
}

/// Playlist object in shortcut response
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ShortcutPlaylist {
    pub id: String,

    pub name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub image_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub cover_url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub playlist_clips: Option<Vec<DiscoverClip>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub playlist_clips_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<DateTime<Utc>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<DateTime<Utc>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub is_liked: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub upvote_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub play_count: Option<i32>,
}
