//! Animation system tests

use std::time::Duration;

use super::*;
use crate::style::animation::easing;

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

    // Easing function tests
    #[test]
    fn test_easing_linear() {
        assert_eq!(easing::linear(0.0), 0.0);
        assert_eq!(easing::linear(0.5), 0.5);
        assert_eq!(easing::linear(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_in() {
        assert_eq!(easing::ease_in(0.0), 0.0);
        assert!(easing::ease_in(0.5) < 0.5);
        assert_eq!(easing::ease_in(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_out() {
        assert_eq!(easing::ease_out(0.0), 0.0);
        assert!(easing::ease_out(0.5) > 0.5);
        assert_eq!(easing::ease_out(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_in_out() {
        assert_eq!(easing::ease_in_out(0.0), 0.0);
        assert!((easing::ease_in_out(0.5) - 0.5).abs() < 0.01);
        assert_eq!(easing::ease_in_out(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_in_out_first_half() {
        // t < 0.5 branch
        let result = easing::ease_in_out(0.25);
        assert!(result < 0.25);
    }

    #[test]
    fn test_easing_ease_in_out_second_half() {
        // t >= 0.5 branch
        let result = easing::ease_in_out(0.75);
        assert!(result > 0.75);
    }

    #[test]
    fn test_easing_ease_in_cubic() {
        assert_eq!(easing::ease_in_cubic(0.0), 0.0);
        assert!(easing::ease_in_cubic(0.5) < 0.5);
        assert_eq!(easing::ease_in_cubic(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_out_cubic() {
        assert_eq!(easing::ease_out_cubic(0.0), 0.0);
        assert!(easing::ease_out_cubic(0.5) > 0.5);
        assert_eq!(easing::ease_out_cubic(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_in_out_cubic() {
        assert_eq!(easing::ease_in_out_cubic(0.0), 0.0);
        assert!((easing::ease_in_out_cubic(0.5) - 0.5).abs() < 0.01);
        assert_eq!(easing::ease_in_out_cubic(1.0), 1.0);
    }

    #[test]
    fn test_easing_ease_in_out_cubic_first_half() {
        let result = easing::ease_in_out_cubic(0.25);
        assert!(result < 0.25);
    }

    #[test]
    fn test_easing_ease_in_out_cubic_second_half() {
        let result = easing::ease_in_out_cubic(0.75);
        assert!(result > 0.75);
    }

    #[test]
    fn test_easing_bounce_out() {
        assert_eq!(easing::bounce_out(0.0), 0.0);
        assert!(easing::bounce_out(1.0) > 0.99);
    }

    #[test]
    fn test_easing_bounce_out_branches() {
        // First branch: t < 1/2.75
        let r1 = easing::bounce_out(0.2);
        assert!(r1 > 0.0 && r1 < 1.0);

        // Second branch: t < 2/2.75
        let r2 = easing::bounce_out(0.5);
        assert!(r2 > 0.0 && r2 < 1.0);

        // Third branch: t < 2.5/2.75
        let r3 = easing::bounce_out(0.85);
        assert!(r3 > 0.0 && r3 < 1.0);

        // Fourth branch: t >= 2.5/2.75
        let r4 = easing::bounce_out(0.95);
        assert!(r4 > 0.0 && r4 <= 1.0);
    }

    #[test]
    fn test_easing_elastic_out() {
        assert_eq!(easing::elastic_out(0.0), 0.0);
        assert_eq!(easing::elastic_out(1.0), 1.0);
    }

    #[test]
    fn test_easing_elastic_out_middle() {
        let result = easing::elastic_out(0.5);
        assert!(result > 0.9); // Elastic overshoots
    }

    #[test]
    fn test_easing_back_out() {
        assert!((easing::back_out(0.0) - 0.0).abs() < 0.001); // Starts at 0
        assert!(easing::back_out(0.5) > 1.0); // Overshoots past 1 mid-animation
        assert!((easing::back_out(1.0) - 1.0).abs() < 0.001); // Ends at 1
    }

    // AnimationState tests
    #[test]
    fn test_animation_state_eq() {
        assert_eq!(AnimationState::Pending, AnimationState::Pending);
        assert_eq!(AnimationState::Running, AnimationState::Running);
        assert_eq!(AnimationState::Paused, AnimationState::Paused);
        assert_eq!(AnimationState::Completed, AnimationState::Completed);
    }

    #[test]
    fn test_animation_state_clone() {
        let state = AnimationState::Running;
        let cloned = state.clone();
        assert_eq!(state, cloned);
    }

    // Tween tests
    #[test]
    fn test_tween_new() {
        let tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        assert_eq!(tween.state(), AnimationState::Pending);
    }

    #[test]
    fn test_tween_default() {
        let tween = Tween::default();
        assert_eq!(tween.from, 0.0);
        assert_eq!(tween.to, 1.0);
        assert_eq!(tween.duration, Duration::from_millis(300));
    }

    #[test]
    fn test_tween_builder_easing() {
        let tween = Tween::new(0.0, 1.0, Duration::from_millis(100)).easing(easing::ease_in);
        // Verify it doesn't crash and returns self
        assert_eq!(tween.state(), AnimationState::Pending);
    }

    #[test]
    fn test_tween_builder_delay() {
        let tween =
            Tween::new(0.0, 1.0, Duration::from_millis(100)).delay(Duration::from_millis(50));
        assert_eq!(tween.delay, Duration::from_millis(50));
    }

    #[test]
    fn test_tween_builder_repeat() {
        let tween = Tween::new(0.0, 1.0, Duration::from_millis(100)).repeat(3);
        assert_eq!(tween.repeat, 3);
    }

    #[test]
    fn test_tween_builder_reverse() {
        let tween = Tween::new(0.0, 1.0, Duration::from_millis(100)).reverse(true);
        assert!(tween.reverse);
    }

    #[test]
    fn test_tween_start() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_millis(100));
        tween.start();
        assert_eq!(tween.state(), AnimationState::Running);
        assert!(tween.is_running());
    }

    #[test]
    fn test_tween_pause() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        tween.start();
        tween.pause();
        assert_eq!(tween.state(), AnimationState::Paused);
        assert!(!tween.is_running());
    }

    #[test]
    fn test_tween_pause_not_running() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        tween.pause(); // Should do nothing
        assert_eq!(tween.state(), AnimationState::Pending);
    }

    #[test]
    fn test_tween_resume() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        tween.start();
        tween.pause();
        tween.resume();
        assert_eq!(tween.state(), AnimationState::Running);
    }

    #[test]
    fn test_tween_resume_not_paused() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        tween.start();
        tween.resume(); // Should do nothing
        assert_eq!(tween.state(), AnimationState::Running);
    }

    #[test]
    fn test_tween_reset() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_millis(100));
        tween.start();
        tween.reset();
        assert_eq!(tween.state(), AnimationState::Pending);
        assert!(!tween.is_running());
        assert!(!tween.is_completed());
    }

    #[test]
    fn test_tween_value_pending() {
        let mut tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        assert_eq!(tween.value(), 0.0); // Pending returns initial 'from' value
    }

    #[test]
    fn test_tween_progress_pending() {
        let tween = Tween::new(0.0, 100.0, Duration::from_secs(1));
        assert_eq!(tween.progress(), 0.0);
    }

    #[test]
    fn test_tween_is_completed() {
        let tween = Tween::new(0.0, 100.0, Duration::from_millis(1));
        assert!(!tween.is_completed());
    }

    // Animation tests
    #[test]
    fn test_animation_new() {
        let anim = Animation::new("test");
        assert_eq!(anim.name, "test");
        assert!(anim.tweens.is_empty());
    }

    #[test]
    fn test_animation_tween() {
        let anim = Animation::new("test").tween(Tween::new(0.0, 1.0, Duration::from_secs(1)));
        assert_eq!(anim.tweens.len(), 1);
    }

    #[test]
    fn test_animation_multiple_tweens() {
        let anim = Animation::new("test")
            .tween(Tween::new(0.0, 1.0, Duration::from_secs(1)))
            .tween(Tween::new(1.0, 0.0, Duration::from_secs(1)));
        assert_eq!(anim.tweens.len(), 2);
    }

    #[test]
    fn test_animation_start() {
        let mut anim = Animation::new("test").tween(Tween::new(0.0, 1.0, Duration::from_secs(1)));
        anim.start();
        assert!(anim.tweens[0].is_running());
    }

    #[test]
    fn test_animation_pause() {
        let mut anim = Animation::new("test").tween(Tween::new(0.0, 1.0, Duration::from_secs(1)));
        anim.start();
        anim.pause();
        assert_eq!(anim.tweens[0].state(), AnimationState::Paused);
    }

    #[test]
    fn test_animation_resume() {
        let mut anim = Animation::new("test").tween(Tween::new(0.0, 1.0, Duration::from_secs(1)));
        anim.start();
        anim.pause();
        anim.resume();
        assert!(anim.tweens[0].is_running());
    }

    #[test]
    fn test_animation_reset() {
        let mut anim = Animation::new("test").tween(Tween::new(0.0, 1.0, Duration::from_secs(1)));
        anim.start();
        anim.reset();
        assert_eq!(anim.tweens[0].state(), AnimationState::Pending);
    }

    #[test]
    fn test_animation_is_completed_empty() {
        let anim = Animation::new("test");
        assert!(anim.is_completed()); // Empty animation is complete
    }

    // Animations presets tests
    #[test]
    fn test_animations_fade_in() {
        let tween = Animations::fade_in(Duration::from_millis(300));
        assert_eq!(tween.from, 0.0);
        assert_eq!(tween.to, 1.0);
    }

    #[test]
    fn test_animations_fade_out() {
        let tween = Animations::fade_out(Duration::from_millis(300));
        assert_eq!(tween.from, 1.0);
        assert_eq!(tween.to, 0.0);
    }

    #[test]
    fn test_animations_slide_in_left() {
        let tween = Animations::slide_in_left(100.0, Duration::from_millis(300));
        assert_eq!(tween.from, -100.0);
        assert_eq!(tween.to, 0.0);
    }

    #[test]
    fn test_animations_slide_in_right() {
        let tween = Animations::slide_in_right(100.0, Duration::from_millis(300));
        assert_eq!(tween.from, 100.0);
        assert_eq!(tween.to, 0.0);
    }

    #[test]
    fn test_animations_scale_up() {
        let tween = Animations::scale_up(Duration::from_millis(300));
        assert_eq!(tween.from, 0.0);
        assert_eq!(tween.to, 1.0);
    }

    #[test]
    fn test_animations_bounce() {
        let tween = Animations::bounce(Duration::from_millis(300));
        assert_eq!(tween.from, 0.0);
        assert_eq!(tween.to, 1.0);
    }

    #[test]
    fn test_animations_pulse() {
        let tween = Animations::pulse(Duration::from_millis(300));
        assert_eq!(tween.from, 1.0);
        assert_eq!(tween.to, 1.2);
        assert!(tween.reverse);
        assert_eq!(tween.repeat, u32::MAX);
    }

    // CssKeyframe tests
    #[test]
    fn test_css_keyframe_new() {
        let kf = CssKeyframe::new(50);
        assert_eq!(kf.percent, 50);
        assert!(kf.properties.is_empty());
    }

    #[test]
    fn test_css_keyframe_new_clamped() {
        let kf = CssKeyframe::new(150); // Over 100
        assert_eq!(kf.percent, 100);
    }

    #[test]
    fn test_css_keyframe_set() {
        let kf = CssKeyframe::new(0).set("opacity", 0.5);
        assert_eq!(kf.get("opacity"), Some(0.5));
    }

    #[test]
    fn test_css_keyframe_get_missing() {
        let kf = CssKeyframe::new(0);
        assert_eq!(kf.get("opacity"), None);
    }

    #[test]
    fn test_css_keyframe_multiple_properties() {
        let kf = CssKeyframe::new(50)
            .set("opacity", 1.0)
            .set("scale", 1.5)
            .set("x", 100.0);
        assert_eq!(kf.get("opacity"), Some(1.0));
        assert_eq!(kf.get("scale"), Some(1.5));
        assert_eq!(kf.get("x"), Some(100.0));
    }

    // KeyframeAnimation tests
    #[test]
    fn test_keyframe_animation_new() {
        let anim = KeyframeAnimation::new("test");
        assert_eq!(anim.name(), "test");
        assert_eq!(anim.state(), AnimationState::Pending);
    }

    #[test]
    fn test_keyframe_animation_keyframe() {
        let anim = KeyframeAnimation::new("test")
            .keyframe(0, |kf| kf.set("opacity", 0.0))
            .keyframe(100, |kf| kf.set("opacity", 1.0));
        // Keyframes are stored
        assert!(!anim.is_running());
    }

    #[test]
    fn test_keyframe_animation_duration() {
        let anim = KeyframeAnimation::new("test").duration(Duration::from_millis(500));
        assert_eq!(anim.duration, Duration::from_millis(500));
    }

    #[test]
    fn test_keyframe_animation_delay() {
        let anim = KeyframeAnimation::new("test").delay(Duration::from_millis(100));
        assert_eq!(anim.delay, Duration::from_millis(100));
    }

    #[test]
    fn test_keyframe_animation_easing() {
        let anim = KeyframeAnimation::new("test").easing(easing::ease_out);
        assert_eq!(anim.state(), AnimationState::Pending);
    }

    #[test]
    fn test_keyframe_animation_iterations() {
        let anim = KeyframeAnimation::new("test").iterations(3);
        assert_eq!(anim.iterations, 3);
    }

    #[test]
    fn test_keyframe_animation_infinite() {
        let anim = KeyframeAnimation::new("test").infinite();
        assert_eq!(anim.iterations, 0);
    }

    #[test]
    fn test_keyframe_animation_direction() {
        let anim = KeyframeAnimation::new("test").direction(AnimationDirection::Reverse);
        assert_eq!(anim.direction, AnimationDirection::Reverse);
    }

    #[test]
    fn test_keyframe_animation_fill_mode() {
        let anim = KeyframeAnimation::new("test").fill_mode(AnimationFillMode::Forwards);
        assert_eq!(anim.fill_mode, AnimationFillMode::Forwards);
    }

    #[test]
    fn test_keyframe_animation_pause_resume() {
        let mut anim = KeyframeAnimation::new("test").duration(Duration::from_secs(1));
        anim.start();
        anim.pause();
        assert_eq!(anim.state(), AnimationState::Paused);
        anim.resume();
        assert!(anim.is_running());
    }

    #[test]
    fn test_keyframe_animation_reset() {
        let mut anim = KeyframeAnimation::new("test");
        anim.start();
        anim.reset();
        assert_eq!(anim.state(), AnimationState::Pending);
    }

    #[test]
    fn test_keyframe_animation_progress_pending() {
        let anim = KeyframeAnimation::new("test");
        assert_eq!(anim.progress(), 0.0);
    }

    // AnimationDirection tests
    #[test]
    fn test_animation_direction_default() {
        let dir = AnimationDirection::default();
        assert_eq!(dir, AnimationDirection::Normal);
    }

    #[test]
    fn test_animation_direction_variants() {
        assert_eq!(AnimationDirection::Normal, AnimationDirection::Normal);
        assert_eq!(AnimationDirection::Reverse, AnimationDirection::Reverse);
        assert_eq!(AnimationDirection::Alternate, AnimationDirection::Alternate);
        assert_eq!(
            AnimationDirection::AlternateReverse,
            AnimationDirection::AlternateReverse
        );
    }

    // AnimationFillMode tests
    #[test]
    fn test_animation_fill_mode_default() {
        let fill = AnimationFillMode::default();
        assert_eq!(fill, AnimationFillMode::None);
    }

    #[test]
    fn test_animation_fill_mode_variants() {
        assert_eq!(AnimationFillMode::None, AnimationFillMode::None);
        assert_eq!(AnimationFillMode::Forwards, AnimationFillMode::Forwards);
        assert_eq!(AnimationFillMode::Backwards, AnimationFillMode::Backwards);
        assert_eq!(AnimationFillMode::Both, AnimationFillMode::Both);
    }

    // Stagger tests
    #[test]
    fn test_stagger() {
        let stagger = Stagger::new(5, Duration::from_millis(50));
        assert_eq!(stagger.delay_for(0), Duration::ZERO);
        assert_eq!(stagger.delay_for(1), Duration::from_millis(50));
        assert_eq!(stagger.delay_for(4), Duration::from_millis(200));
    }

    // AnimationGroup tests
    #[test]
    fn test_animation_group_parallel() {
        let group = AnimationGroup::parallel()
            .with_animation(KeyframeAnimation::new("a").duration(Duration::from_millis(100)))
            .with_animation(KeyframeAnimation::new("b").duration(Duration::from_millis(200)));

        assert_eq!(group.total_duration(), Duration::from_millis(200));
    }

    #[test]
    fn test_animation_group_sequential() {
        let group = AnimationGroup::sequential()
            .with_animation(KeyframeAnimation::new("a").duration(Duration::from_millis(100)))
            .with_animation(KeyframeAnimation::new("b").duration(Duration::from_millis(200)));

        assert_eq!(group.total_duration(), Duration::from_millis(300));
    }
}
