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

// =============================================================================
// GET SIMILAR CLIPS
// =============================================================================

/// Response from /api/clips/get_similar endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SimilarClipsResponse {
    pub clips: Vec<SimilarClip>,

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

/// Clip in similar clips response
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SimilarClip {
    pub id: Uuid,

    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>,

    pub user_id: Uuid,

    pub handle: String,

    pub display_name: String,

    pub play_count: i32,

    pub upvote_count: i32,

    pub created_at: DateTime<Utc>,

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

// =============================================================================
// GET SONGS BY IDS
// =============================================================================

/// Response from /api/clips/get_songs_by_ids endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GetSongsByIdsResponse {
    pub clips: Vec<SimilarClip>,
}

// =============================================================================
// GET TRASHED CLIPS
// =============================================================================

/// Response from /api/clips/trashed_v2 endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TrashedClipsResponse {
    pub clips: Vec<TrashedClip>,

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

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

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

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

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

/// Trashed clip item
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TrashedClip {
    pub id: Uuid,

    pub title: String,

    #[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 created_at: Option<DateTime<Utc>>,

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

// =============================================================================
// GET CLIP PROMPTS
// =============================================================================

/// Response from /api/clips/clip_prompts/ endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ClipPromptsResponse {
    pub prompts: Vec<ClipPrompt>,

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

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

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

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

/// Individual saved prompt
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ClipPrompt {
    pub id: String,

    pub text: String,

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

    #[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 usage_count: Option<i32>,
}

// =============================================================================
// GET DIRECT CHILDREN
// =============================================================================

/// Response from /api/clips/direct_children_by_user endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DirectChildrenByUserResponse {
    pub children_by_user: Vec<ChildrenByUserGroup>,

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

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

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

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

/// Remixes grouped by user
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ChildrenByUserGroup {
    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 clips: Vec<ChildClip>,

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

/// Child/remix clip
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ChildClip {
    pub id: Uuid,

    pub title: String,

    pub created_at: DateTime<Utc>,

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

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

// =============================================================================
// GET DIRECT CHILDREN COUNT
// =============================================================================

/// Response from /api/clips/direct_children_count endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DirectChildrenCountResponse {
    pub count: i32,

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

// =============================================================================
// GET CHILDREN (Full Tree)
// =============================================================================

/// Response from /api/clips/children endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ChildrenResponse {
    pub root_clip: Option<ChildClip>,

    pub children: Vec<ChildrenNode>,

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

/// Node in children tree
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ChildrenNode {
    pub clip: ChildClip,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub children: Option<Vec<Box<ChildrenNode>>>,

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

// =============================================================================
// GET ALIGNED CLIPS
// =============================================================================

/// Response from /api/clips/aligned_clips endpoint
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AlignedClipsResponse {
    pub aligned_clips: Vec<AlignedClip>,

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

/// Aligned clip (matching audio alignment)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AlignedClip {
    pub id: Uuid,

    pub title: String,

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

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

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

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