use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::schema::{AnimatableValue, Transform};
use crate::schema::transition::TransitionSpec;

/// Fill mode for layers — equivalent to Remotion's `<AbsoluteFill>`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FillMode {
    Parent,
}

/// A single layer in a composition.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Layer {
    pub id: String,
    /// Frame this layer becomes active (inclusive).
    #[serde(rename = "in")]
    pub in_point: u64,
    /// Frame this layer becomes inactive (exclusive).
    #[serde(rename = "out")]
    pub out_point: u64,
    pub transform: Transform,
    /// When set to `"parent"`, the layer fills the entire canvas (position/rotation/scale ignored).
    #[serde(default)]
    pub fill: Option<FillMode>,
    #[serde(flatten)]
    pub content: LayerContent,
}

/// Type-specific layer content, discriminated by the `"type"` JSON field.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum LayerContent {
    Solid {
        color: String,
    },
    Image {
        src: String,
    },
    Video {
        src: String,
        #[serde(default)]
        trim_start: f64,
        #[serde(default)]
        trim_end: Option<f64>,
    },
    Text {
        text: String,
        font: FontSpec,
        #[serde(default = "default_center_align")]
        align: TextAlign,
    },
    Audio {
        src: String,
        #[serde(default = "default_one_anim")]
        volume: AnimatableValue<f64>,
    },
    Lottie {
        src: String,
    },
    Composition {
        #[serde(rename = "composition_id")]
        id: String,
    },
    Shape {
        shape: ShapeSpec,
    },
    Gradient {
        gradient: GradientSpec,
    },
}

fn default_center_align() -> TextAlign {
    TextAlign::Center
}

fn default_one_anim() -> AnimatableValue<f64> {
    AnimatableValue::Static(1.0)
}

/// Font specification for text layers.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FontSpec {
    pub family: String,
    #[serde(default = "default_font_size")]
    pub size: f64,
    #[serde(default = "default_font_weight")]
    pub weight: u32,
    #[serde(default = "default_white")]
    pub color: String,
}

fn default_font_size() -> f64 {
    32.0
}

fn default_font_weight() -> u32 {
    400
}

fn default_white() -> String {
    "#ffffff".into()
}

/// Text alignment.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum TextAlign {
    Left,
    #[default]
    Center,
    Right,
}

/// Shape specification.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "shape_type", rename_all = "snake_case")]
pub enum ShapeSpec {
    Rect {
        width: f64,
        height: f64,
        corner_radius: Option<f64>,
        fill: Option<String>,
        stroke: Option<StrokeSpec>,
    },
    Ellipse {
        width: f64,
        height: f64,
        fill: Option<String>,
        stroke: Option<StrokeSpec>,
    },
    Line {
        x1: f64,
        y1: f64,
        x2: f64,
        y2: f64,
        stroke: StrokeSpec,
    },
    Polygon {
        points: Vec<[f64; 2]>,
        fill: Option<String>,
        stroke: Option<StrokeSpec>,
    },
}

/// Stroke specification.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrokeSpec {
    pub color: String,
    pub width: f64,
}

/// Gradient specification — linear or radial.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "gradient_type", rename_all = "snake_case")]
pub enum GradientSpec {
    Linear {
        start: [f64; 2],
        end: [f64; 2],
        colors: Vec<GradientStop>,
    },
    Radial {
        center: [f64; 2],
        radius: f64,
        colors: Vec<GradientStop>,
    },
}

/// A single color stop in a gradient.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GradientStop {
    pub offset: f64,
    pub color: String,
}

/// A composition — an ordered list of layers (first = bottom of visual stack).
///
/// When `sequence` is `true`, layers play back-to-back instead of using their
/// individual `in`/`out` points for global timing. An optional `transition`
/// controls overlap between consecutive layers.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Composition {
    pub layers: Vec<Layer>,
    #[serde(default)]
    pub sequence: bool,
    #[serde(default)]
    pub transition: Option<TransitionSpec>,
}

/// Map of composition ID to composition.
pub type Compositions = HashMap<String, Composition>;
