//! Layer system for Lightningbeam
//!
//! Layers organize objects and shapes, and contain animation data.

use crate::animation::AnimationData;
use crate::clip::ClipInstance;
use crate::effect_layer::EffectLayer;
use crate::object::ShapeInstance;
use crate::shape::Shape;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// Layer type
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum LayerType {
    /// Vector graphics layer (shapes and objects)
    Vector,
    /// Audio track
    Audio,
    /// Video clip
    Video,
    /// Generic automation layer
    Automation,
    /// Visual effects layer
    Effect,
}

/// Common trait for all layer types
///
/// Provides uniform access to common layer properties across VectorLayer,
/// AudioLayer, VideoLayer, and their wrapper AnyLayer enum.
pub trait LayerTrait {
    // Identity
    fn id(&self) -> Uuid;
    fn name(&self) -> &str;
    fn set_name(&mut self, name: String);
    fn has_custom_name(&self) -> bool;
    fn set_has_custom_name(&mut self, custom: bool);

    // Visual properties
    fn visible(&self) -> bool;
    fn set_visible(&mut self, visible: bool);
    fn opacity(&self) -> f64;
    fn set_opacity(&mut self, opacity: f64);

    // Audio properties (all layers can affect audio through nesting)
    fn volume(&self) -> f64;
    fn set_volume(&mut self, volume: f64);
    fn muted(&self) -> bool;
    fn set_muted(&mut self, muted: bool);

    // Editor state
    fn soloed(&self) -> bool;
    fn set_soloed(&mut self, soloed: bool);
    fn locked(&self) -> bool;
    fn set_locked(&mut self, locked: bool);
}

/// Base layer structure
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Layer {
    /// Unique identifier
    pub id: Uuid,

    /// Layer type
    pub layer_type: LayerType,

    /// Layer name
    pub name: String,

    /// Whether the name was set by user (vs auto-generated)
    pub has_custom_name: bool,

    /// Whether the layer is visible
    pub visible: bool,

    /// Layer opacity (0.0 to 1.0)
    pub opacity: f64,

    /// Audio volume (1.0 = 100%, affects nested audio layers/clips)
    pub volume: f64,

    /// Audio mute state
    pub muted: bool,

    /// Solo state (for isolating layers)
    pub soloed: bool,

    /// Lock state (prevents editing)
    pub locked: bool,

    /// Animation data for this layer
    pub animation_data: AnimationData,
}

impl Layer {
    /// Create a new layer
    pub fn new(layer_type: LayerType, name: impl Into<String>) -> Self {
        Self {
            id: Uuid::new_v4(),
            layer_type,
            name: name.into(),
            has_custom_name: false,  // Auto-generated by default
            visible: true,
            opacity: 1.0,
            volume: 1.0,             // 100% volume
            muted: false,
            soloed: false,
            locked: false,
            animation_data: AnimationData::new(),
        }
    }

    /// Create with a specific ID
    pub fn with_id(id: Uuid, layer_type: LayerType, name: impl Into<String>) -> Self {
        Self {
            id,
            layer_type,
            name: name.into(),
            has_custom_name: false,
            visible: true,
            opacity: 1.0,
            volume: 1.0,
            muted: false,
            soloed: false,
            locked: false,
            animation_data: AnimationData::new(),
        }
    }

    /// Set visibility
    pub fn with_visibility(mut self, visible: bool) -> Self {
        self.visible = visible;
        self
    }
}

/// Vector layer containing shapes and objects
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VectorLayer {
    /// Base layer properties
    pub layer: Layer,

    /// Shapes defined in this layer (indexed by UUID for O(1) lookup)
    pub shapes: HashMap<Uuid, Shape>,

    /// Shape instances (references to shapes with transforms)
    pub shape_instances: Vec<ShapeInstance>,

    /// Clip instances (references to vector clips with transforms)
    /// VectorLayer can contain instances of VectorClips for nested compositions
    pub clip_instances: Vec<ClipInstance>,
}

impl LayerTrait for VectorLayer {
    fn id(&self) -> Uuid {
        self.layer.id
    }

    fn name(&self) -> &str {
        &self.layer.name
    }

    fn set_name(&mut self, name: String) {
        self.layer.name = name;
    }

    fn has_custom_name(&self) -> bool {
        self.layer.has_custom_name
    }

    fn set_has_custom_name(&mut self, custom: bool) {
        self.layer.has_custom_name = custom;
    }

    fn visible(&self) -> bool {
        self.layer.visible
    }

    fn set_visible(&mut self, visible: bool) {
        self.layer.visible = visible;
    }

    fn opacity(&self) -> f64 {
        self.layer.opacity
    }

    fn set_opacity(&mut self, opacity: f64) {
        self.layer.opacity = opacity;
    }

    fn volume(&self) -> f64 {
        self.layer.volume
    }

    fn set_volume(&mut self, volume: f64) {
        self.layer.volume = volume;
    }

    fn muted(&self) -> bool {
        self.layer.muted
    }

    fn set_muted(&mut self, muted: bool) {
        self.layer.muted = muted;
    }

    fn soloed(&self) -> bool {
        self.layer.soloed
    }

    fn set_soloed(&mut self, soloed: bool) {
        self.layer.soloed = soloed;
    }

    fn locked(&self) -> bool {
        self.layer.locked
    }

    fn set_locked(&mut self, locked: bool) {
        self.layer.locked = locked;
    }
}

impl VectorLayer {
    /// Create a new vector layer
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            layer: Layer::new(LayerType::Vector, name),
            shapes: HashMap::new(),
            shape_instances: Vec::new(),
            clip_instances: Vec::new(),
        }
    }

    /// Add a shape to this layer
    pub fn add_shape(&mut self, shape: Shape) -> Uuid {
        let id = shape.id;
        self.shapes.insert(id, shape);
        id
    }

    /// Add an object to this layer
    pub fn add_object(&mut self, object: ShapeInstance) -> Uuid {
        let id = object.id;
        self.shape_instances.push(object);
        id
    }

    /// Find a shape by ID
    pub fn get_shape(&self, id: &Uuid) -> Option<&Shape> {
        self.shapes.get(id)
    }

    /// Find a mutable shape by ID
    pub fn get_shape_mut(&mut self, id: &Uuid) -> Option<&mut Shape> {
        self.shapes.get_mut(id)
    }

    /// Find an object by ID
    pub fn get_object(&self, id: &Uuid) -> Option<&ShapeInstance> {
        self.shape_instances.iter().find(|o| &o.id == id)
    }

    /// Find a mutable object by ID
    pub fn get_object_mut(&mut self, id: &Uuid) -> Option<&mut ShapeInstance> {
        self.shape_instances.iter_mut().find(|o| &o.id == id)
    }

    // === MUTATION METHODS (pub(crate) - only accessible to action module) ===

    /// Add a shape to this layer (internal, for actions only)
    ///
    /// This method is intentionally `pub(crate)` to ensure mutations
    /// only happen through the action system.
    pub(crate) fn add_shape_internal(&mut self, shape: Shape) -> Uuid {
        let id = shape.id;
        self.shapes.insert(id, shape);
        id
    }

    /// Add an object to this layer (internal, for actions only)
    ///
    /// This method is intentionally `pub(crate)` to ensure mutations
    /// only happen through the action system.
    pub(crate) fn add_object_internal(&mut self, object: ShapeInstance) -> Uuid {
        let id = object.id;
        self.shape_instances.push(object);
        id
    }

    /// Remove a shape from this layer (internal, for actions only)
    ///
    /// Returns the removed shape if found.
    /// This method is intentionally `pub(crate)` to ensure mutations
    /// only happen through the action system.
    pub(crate) fn remove_shape_internal(&mut self, id: &Uuid) -> Option<Shape> {
        self.shapes.remove(id)
    }

    /// Remove an object from this layer (internal, for actions only)
    ///
    /// Returns the removed object if found.
    /// This method is intentionally `pub(crate)` to ensure mutations
    /// only happen through the action system.
    pub(crate) fn remove_object_internal(&mut self, id: &Uuid) -> Option<ShapeInstance> {
        if let Some(index) = self.shape_instances.iter().position(|o| &o.id == id) {
            Some(self.shape_instances.remove(index))
        } else {
            None
        }
    }

    /// Modify an object in place (internal, for actions only)
    ///
    /// Applies the given function to the object if found.
    /// This method is intentionally `pub(crate)` to ensure mutations
    /// only happen through the action system.
    pub fn modify_object_internal<F>(&mut self, id: &Uuid, f: F)
    where
        F: FnOnce(&mut ShapeInstance),
    {
        if let Some(object) = self.get_object_mut(id) {
            f(object);
        }
    }
}

/// Audio layer subtype - distinguishes sampled audio from MIDI
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AudioLayerType {
    /// Sampled audio (WAV, MP3, etc.)
    Sampled,
    /// MIDI sequence
    Midi,
}

impl Default for AudioLayerType {
    fn default() -> Self {
        AudioLayerType::Sampled
    }
}

/// Audio layer containing audio clips
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AudioLayer {
    /// Base layer properties
    pub layer: Layer,

    /// Clip instances (references to audio clips)
    /// AudioLayer can contain instances of AudioClips (sampled or MIDI)
    pub clip_instances: Vec<ClipInstance>,

    /// Audio layer subtype (sampled vs MIDI)
    #[serde(default)]
    pub audio_layer_type: AudioLayerType,
}

impl LayerTrait for AudioLayer {
    fn id(&self) -> Uuid {
        self.layer.id
    }

    fn name(&self) -> &str {
        &self.layer.name
    }

    fn set_name(&mut self, name: String) {
        self.layer.name = name;
    }

    fn has_custom_name(&self) -> bool {
        self.layer.has_custom_name
    }

    fn set_has_custom_name(&mut self, custom: bool) {
        self.layer.has_custom_name = custom;
    }

    fn visible(&self) -> bool {
        self.layer.visible
    }

    fn set_visible(&mut self, visible: bool) {
        self.layer.visible = visible;
    }

    fn opacity(&self) -> f64 {
        self.layer.opacity
    }

    fn set_opacity(&mut self, opacity: f64) {
        self.layer.opacity = opacity;
    }

    fn volume(&self) -> f64 {
        self.layer.volume
    }

    fn set_volume(&mut self, volume: f64) {
        self.layer.volume = volume;
    }

    fn muted(&self) -> bool {
        self.layer.muted
    }

    fn set_muted(&mut self, muted: bool) {
        self.layer.muted = muted;
    }

    fn soloed(&self) -> bool {
        self.layer.soloed
    }

    fn set_soloed(&mut self, soloed: bool) {
        self.layer.soloed = soloed;
    }

    fn locked(&self) -> bool {
        self.layer.locked
    }

    fn set_locked(&mut self, locked: bool) {
        self.layer.locked = locked;
    }
}

impl AudioLayer {
    /// Create a new sampled audio layer
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            layer: Layer::new(LayerType::Audio, name),
            clip_instances: Vec::new(),
            audio_layer_type: AudioLayerType::Sampled,
        }
    }

    /// Create a new sampled audio layer (explicit)
    pub fn new_sampled(name: impl Into<String>) -> Self {
        Self::new(name)
    }

    /// Create a new MIDI layer
    pub fn new_midi(name: impl Into<String>) -> Self {
        Self {
            layer: Layer::new(LayerType::Audio, name),
            clip_instances: Vec::new(),
            audio_layer_type: AudioLayerType::Midi,
        }
    }
}

/// Video layer containing video clips
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VideoLayer {
    /// Base layer properties
    pub layer: Layer,

    /// Clip instances (references to video clips)
    /// VideoLayer can contain instances of VideoClips
    pub clip_instances: Vec<ClipInstance>,
}

impl LayerTrait for VideoLayer {
    fn id(&self) -> Uuid {
        self.layer.id
    }

    fn name(&self) -> &str {
        &self.layer.name
    }

    fn set_name(&mut self, name: String) {
        self.layer.name = name;
    }

    fn has_custom_name(&self) -> bool {
        self.layer.has_custom_name
    }

    fn set_has_custom_name(&mut self, custom: bool) {
        self.layer.has_custom_name = custom;
    }

    fn visible(&self) -> bool {
        self.layer.visible
    }

    fn set_visible(&mut self, visible: bool) {
        self.layer.visible = visible;
    }

    fn opacity(&self) -> f64 {
        self.layer.opacity
    }

    fn set_opacity(&mut self, opacity: f64) {
        self.layer.opacity = opacity;
    }

    fn volume(&self) -> f64 {
        self.layer.volume
    }

    fn set_volume(&mut self, volume: f64) {
        self.layer.volume = volume;
    }

    fn muted(&self) -> bool {
        self.layer.muted
    }

    fn set_muted(&mut self, muted: bool) {
        self.layer.muted = muted;
    }

    fn soloed(&self) -> bool {
        self.layer.soloed
    }

    fn set_soloed(&mut self, soloed: bool) {
        self.layer.soloed = soloed;
    }

    fn locked(&self) -> bool {
        self.layer.locked
    }

    fn set_locked(&mut self, locked: bool) {
        self.layer.locked = locked;
    }
}

impl VideoLayer {
    /// Create a new video layer
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            layer: Layer::new(LayerType::Video, name),
            clip_instances: Vec::new(),
        }
    }
}

/// Unified layer enum for polymorphic handling
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AnyLayer {
    Vector(VectorLayer),
    Audio(AudioLayer),
    Video(VideoLayer),
    Effect(EffectLayer),
}

impl LayerTrait for AnyLayer {
    fn id(&self) -> Uuid {
        match self {
            AnyLayer::Vector(l) => l.id(),
            AnyLayer::Audio(l) => l.id(),
            AnyLayer::Video(l) => l.id(),
            AnyLayer::Effect(l) => l.id(),
        }
    }

    fn name(&self) -> &str {
        match self {
            AnyLayer::Vector(l) => l.name(),
            AnyLayer::Audio(l) => l.name(),
            AnyLayer::Video(l) => l.name(),
            AnyLayer::Effect(l) => l.name(),
        }
    }

    fn set_name(&mut self, name: String) {
        match self {
            AnyLayer::Vector(l) => l.set_name(name),
            AnyLayer::Audio(l) => l.set_name(name),
            AnyLayer::Video(l) => l.set_name(name),
            AnyLayer::Effect(l) => l.set_name(name),
        }
    }

    fn has_custom_name(&self) -> bool {
        match self {
            AnyLayer::Vector(l) => l.has_custom_name(),
            AnyLayer::Audio(l) => l.has_custom_name(),
            AnyLayer::Video(l) => l.has_custom_name(),
            AnyLayer::Effect(l) => l.has_custom_name(),
        }
    }

    fn set_has_custom_name(&mut self, custom: bool) {
        match self {
            AnyLayer::Vector(l) => l.set_has_custom_name(custom),
            AnyLayer::Audio(l) => l.set_has_custom_name(custom),
            AnyLayer::Video(l) => l.set_has_custom_name(custom),
            AnyLayer::Effect(l) => l.set_has_custom_name(custom),
        }
    }

    fn visible(&self) -> bool {
        match self {
            AnyLayer::Vector(l) => l.visible(),
            AnyLayer::Audio(l) => l.visible(),
            AnyLayer::Video(l) => l.visible(),
            AnyLayer::Effect(l) => l.visible(),
        }
    }

    fn set_visible(&mut self, visible: bool) {
        match self {
            AnyLayer::Vector(l) => l.set_visible(visible),
            AnyLayer::Audio(l) => l.set_visible(visible),
            AnyLayer::Video(l) => l.set_visible(visible),
            AnyLayer::Effect(l) => l.set_visible(visible),
        }
    }

    fn opacity(&self) -> f64 {
        match self {
            AnyLayer::Vector(l) => l.opacity(),
            AnyLayer::Audio(l) => l.opacity(),
            AnyLayer::Video(l) => l.opacity(),
            AnyLayer::Effect(l) => l.opacity(),
        }
    }

    fn set_opacity(&mut self, opacity: f64) {
        match self {
            AnyLayer::Vector(l) => l.set_opacity(opacity),
            AnyLayer::Audio(l) => l.set_opacity(opacity),
            AnyLayer::Video(l) => l.set_opacity(opacity),
            AnyLayer::Effect(l) => l.set_opacity(opacity),
        }
    }

    fn volume(&self) -> f64 {
        match self {
            AnyLayer::Vector(l) => l.volume(),
            AnyLayer::Audio(l) => l.volume(),
            AnyLayer::Video(l) => l.volume(),
            AnyLayer::Effect(l) => l.volume(),
        }
    }

    fn set_volume(&mut self, volume: f64) {
        match self {
            AnyLayer::Vector(l) => l.set_volume(volume),
            AnyLayer::Audio(l) => l.set_volume(volume),
            AnyLayer::Video(l) => l.set_volume(volume),
            AnyLayer::Effect(l) => l.set_volume(volume),
        }
    }

    fn muted(&self) -> bool {
        match self {
            AnyLayer::Vector(l) => l.muted(),
            AnyLayer::Audio(l) => l.muted(),
            AnyLayer::Video(l) => l.muted(),
            AnyLayer::Effect(l) => l.muted(),
        }
    }

    fn set_muted(&mut self, muted: bool) {
        match self {
            AnyLayer::Vector(l) => l.set_muted(muted),
            AnyLayer::Audio(l) => l.set_muted(muted),
            AnyLayer::Video(l) => l.set_muted(muted),
            AnyLayer::Effect(l) => l.set_muted(muted),
        }
    }

    fn soloed(&self) -> bool {
        match self {
            AnyLayer::Vector(l) => l.soloed(),
            AnyLayer::Audio(l) => l.soloed(),
            AnyLayer::Video(l) => l.soloed(),
            AnyLayer::Effect(l) => l.soloed(),
        }
    }

    fn set_soloed(&mut self, soloed: bool) {
        match self {
            AnyLayer::Vector(l) => l.set_soloed(soloed),
            AnyLayer::Audio(l) => l.set_soloed(soloed),
            AnyLayer::Video(l) => l.set_soloed(soloed),
            AnyLayer::Effect(l) => l.set_soloed(soloed),
        }
    }

    fn locked(&self) -> bool {
        match self {
            AnyLayer::Vector(l) => l.locked(),
            AnyLayer::Audio(l) => l.locked(),
            AnyLayer::Video(l) => l.locked(),
            AnyLayer::Effect(l) => l.locked(),
        }
    }

    fn set_locked(&mut self, locked: bool) {
        match self {
            AnyLayer::Vector(l) => l.set_locked(locked),
            AnyLayer::Audio(l) => l.set_locked(locked),
            AnyLayer::Video(l) => l.set_locked(locked),
            AnyLayer::Effect(l) => l.set_locked(locked),
        }
    }
}

impl AnyLayer {
    /// Get a reference to the base layer
    pub fn layer(&self) -> &Layer {
        match self {
            AnyLayer::Vector(l) => &l.layer,
            AnyLayer::Audio(l) => &l.layer,
            AnyLayer::Video(l) => &l.layer,
            AnyLayer::Effect(l) => &l.layer,
        }
    }

    /// Get a mutable reference to the base layer
    pub fn layer_mut(&mut self) -> &mut Layer {
        match self {
            AnyLayer::Vector(l) => &mut l.layer,
            AnyLayer::Audio(l) => &mut l.layer,
            AnyLayer::Video(l) => &mut l.layer,
            AnyLayer::Effect(l) => &mut l.layer,
        }
    }

    /// Get the layer ID
    pub fn id(&self) -> Uuid {
        self.layer().id
    }

    /// Get the layer name
    pub fn name(&self) -> &str {
        &self.layer().name
    }
}

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

    #[test]
    fn test_layer_creation() {
        let layer = Layer::new(LayerType::Vector, "Test Layer");
        assert_eq!(layer.layer_type, LayerType::Vector);
        assert_eq!(layer.name, "Test Layer");
        assert_eq!(layer.opacity, 1.0);
    }

    #[test]
    fn test_vector_layer() {
        let vector_layer = VectorLayer::new("My Layer");
        assert_eq!(vector_layer.shapes.len(), 0);
        assert_eq!(vector_layer.shape_instances.len(), 0);
    }

    #[test]
    fn test_layer_basic_properties() {
        let layer = Layer::new(LayerType::Vector, "Test");

        assert_eq!(layer.name, "Test");
        assert_eq!(layer.visible, true);
        assert_eq!(layer.opacity, 1.0);
        assert_eq!(layer.volume, 1.0);
        assert_eq!(layer.muted, false);
        assert_eq!(layer.soloed, false);
        assert_eq!(layer.locked, false);
    }
}
