use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChartEasing {
    Linear,
    EaseIn,
    EaseOut,
    EaseInOut,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChartAnimationKind {
    None,
    Grow,
    Fade,
    Sweep,
    Pulse,
    Morph,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChartAnimation {
    pub enabled: bool,
    pub kind: ChartAnimationKind,
    pub duration_ms: u64,
    pub delay_ms: u64,
    pub stagger_ms: u64,
    pub easing: ChartEasing,
    pub reduced_motion_safe: bool,
    pub repeat: bool,
}

impl Default for ChartAnimation {
    fn default() -> Self {
        Self {
            enabled: false,
            kind: ChartAnimationKind::Grow,
            duration_ms: 450,
            delay_ms: 0,
            stagger_ms: 24,
            easing: ChartEasing::EaseOut,
            reduced_motion_safe: true,
            repeat: false,
        }
    }
}

impl ChartAnimation {
    pub fn disabled() -> Self {
        Self {
            enabled: false,
            kind: ChartAnimationKind::None,
            ..Self::default()
        }
    }

    pub fn enter(kind: ChartAnimationKind) -> Self {
        Self {
            enabled: true,
            kind,
            ..Self::default()
        }
    }

    pub fn duration_ms(mut self, duration_ms: u64) -> Self {
        self.duration_ms = duration_ms;
        self
    }

    pub fn delay_ms(mut self, delay_ms: u64) -> Self {
        self.delay_ms = delay_ms;
        self
    }

    pub fn stagger_ms(mut self, stagger_ms: u64) -> Self {
        self.stagger_ms = stagger_ms;
        self
    }

    pub fn easing(mut self, easing: ChartEasing) -> Self {
        self.easing = easing;
        self
    }

    pub fn reduced_motion_safe(mut self, safe: bool) -> Self {
        self.reduced_motion_safe = safe;
        self
    }

    pub fn repeat(mut self, repeat: bool) -> Self {
        self.repeat = repeat;
        self
    }

    pub fn progress_at(&self, elapsed_ms: u64, item_index: usize) -> f32 {
        if !self.enabled || self.duration_ms == 0 {
            return 1.0;
        }
        let start = self
            .delay_ms
            .saturating_add(self.stagger_ms.saturating_mul(item_index as u64));
        if elapsed_ms <= start {
            return 0.0;
        }
        let raw = ((elapsed_ms - start) as f32 / self.duration_ms as f32).clamp(0.0, 1.0);
        match self.easing {
            ChartEasing::Linear => raw,
            ChartEasing::EaseIn => raw * raw,
            ChartEasing::EaseOut => 1.0 - (1.0 - raw) * (1.0 - raw),
            ChartEasing::EaseInOut => {
                if raw < 0.5 {
                    2.0 * raw * raw
                } else {
                    1.0 - (-2.0 * raw + 2.0).powi(2) / 2.0
                }
            }
        }
    }
}
