// display/carousel.rs
// Carousel component - Image/content slider

use hikari_palette::classes::{CarouselClass, ClassesBuilder, TypedClass};

use crate::prelude::*;
use crate::styled::StyledComponent;

pub struct CarouselComponent;

#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum CarouselIndicatorPosition {
    #[default]
    Bottom,
    Top,
    Left,
    Right,
}

#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum CarouselIndicatorType {
    #[default]
    Dots,
    Line,
    Hidden,
}

#[define_props]
#[derive(Debug)]
pub struct CarouselProps {
    #[default]
    pub children: Element,

    #[default(5000)]
    pub autoplay: u64,

    #[default(true)]
    pub show_arrows: bool,

    #[default(CarouselIndicatorPosition::Bottom)]
    pub indicator_position: CarouselIndicatorPosition,

    #[default(CarouselIndicatorType::Dots)]
    pub indicator_type: CarouselIndicatorType,

    #[default(true)]
    pub show_pause: bool,

    #[default(false)]
    pub infinite: bool,

    #[default(false)]
    pub initial_paused: bool,
}

#[component]
pub fn Carousel(props: CarouselProps) -> Element {
    let current_index = use_signal(|| 0);
    let is_paused = use_signal(|| props.initial_paused);

    // Count children without moving props.children
    let total = match &props.children {
        VNode::Fragment(children) => children.len(),
        _ => 0,
    };

    let current_index_for_prev = current_index.clone();
    let handle_prev = move |_| {
        if total == 0 {
            return;
        }
        let mut idx = current_index_for_prev.write();
        *idx = if *idx == 0 { total - 1 } else { *idx - 1 };
    };

    let current_index_for_next = current_index.clone();
    let handle_next = move |_| {
        if total == 0 {
            return;
        }
        let mut idx = current_index_for_next.write();
        *idx = (*idx + 1) % total;
    };

    let current_index_for_dot = current_index.clone();
    let handle_dot_click = move |index: usize| {
        *current_index_for_dot.write() = index;
    };

    let is_paused_for_toggle = is_paused.clone();
    let toggle_pause = move |_| {
        *is_paused_for_toggle.write() = !is_paused_for_toggle.get();
    };

    // Autoplay implementation using platform::set_timeout
    let autoplay_interval = props.autoplay;
    let autoplay_index = current_index.clone();
    let autoplay_paused = is_paused.clone();
    let autoplay_total = total;
    let autoplay_infinite = props.infinite;

    use_effect(move || {
        if autoplay_interval == 0 || autoplay_paused.get() || autoplay_total == 0 {
            return;
        }

        let index_for_timer = autoplay_index.clone();
        let total_for_timer = autoplay_total;
        let infinite_for_timer = autoplay_infinite;

        crate::platform::set_timeout(
            move || {
                let current = index_for_timer.get();
                let next = if infinite_for_timer {
                    (current + 1) % total_for_timer
                } else if current + 1 < total_for_timer {
                    current + 1
                } else {
                    return;
                };
                index_for_timer.set(next);
            },
            autoplay_interval as i32,
        );
    });

    let track_transform = format!(
        "transform: translateX(-{}%);",
        current_index.get() as f64 * 100.0
    );

    let indicator_classes = ClassesBuilder::new()
        .add_typed(CarouselClass::Indicators)
        .add_typed(match props.indicator_position {
            CarouselIndicatorPosition::Bottom | CarouselIndicatorPosition::Top => {
                CarouselClass::IndicatorsDots
            }
            CarouselIndicatorPosition::Left | CarouselIndicatorPosition::Right => {
                CarouselClass::IndicatorsHidden
            }
        })
        .add_typed(match props.indicator_type {
            CarouselIndicatorType::Dots => CarouselClass::IndicatorsDots,
            CarouselIndicatorType::Line => CarouselClass::IndicatorsLine,
            CarouselIndicatorType::Hidden => CarouselClass::IndicatorsHidden,
        })
        .build();

    let prev_arrow_classes = ClassesBuilder::new()
        .add_typed(CarouselClass::Arrow)
        .add_typed(CarouselClass::ArrowPrev)
        .build();

    let next_arrow_classes = ClassesBuilder::new()
        .add_typed(CarouselClass::Arrow)
        .add_typed(CarouselClass::ArrowNext)
        .build();

    // Pre-compute dot elements
    let current_index_for_dots = current_index.clone();
    let dot_elements: Vec<Element> = (0..total)
        .map(move |i| {
            let current_index_in_map = current_index_for_dots.clone();
            let current_index_for_add_if = current_index_in_map.clone();
            let dot_classes = ClassesBuilder::new()
                .add_typed(CarouselClass::Dot)
                .add_typed_if(CarouselClass::DotActive, {
                    i == current_index_for_add_if.get()
                })
                .build();

            let idx_signal = current_index_in_map.clone();
            rsx! {
                button {
                    class: dot_classes,
                    onclick: move |_| {
                        *idx_signal.write() = i;
                    },
                    aria_label: format!("Slide {}", i + 1),
                    disabled: total <= 1,
                }
            }
        })
        .collect();

    rsx! {
        div { class: CarouselClass::Container.class_name(),

            // Track
            div {
                class: CarouselClass::Track.class_name(),
                style: track_transform,
                {props.children}
            }

            // Navigation arrows
            if props.show_arrows {
                button {
                    class: prev_arrow_classes,
                    onclick: handle_prev,
                    disabled: total <= 1,
                    "‹"
                }

                button {
                    class: next_arrow_classes,
                    onclick: handle_next,
                    disabled: total <= 1,
                    "›"
                }
            }

            // Indicator dots
            if props.indicator_type != CarouselIndicatorType::Hidden && total > 1 {
                div { class: indicator_classes, ..dot_elements }
            }

            // Pause button
            if props.show_pause && props.autoplay > 0 && total > 1 {
                button {
                    class: CarouselClass::Pause.class_name(),
                    onclick: toggle_pause,
                    aria_label: if is_paused.get() { "Play" } else { "Pause" },
                    if is_paused.get() {
                        "▶"
                    } else {
                        "⏸"
                    }
                }
            }
        }
    }
}

impl StyledComponent for CarouselComponent {
    fn styles() -> &'static str {
        r#"
.hi-carousel {
    position: relative;
    width: 100%;
    overflow: hidden;
    border-radius: 8px;
    background-color: var(--hi-color-bg-container);
}

[data-theme="dark"] .hi-carousel {
    background-color: var(--hi-surface);
}

.hi-carousel-track {
    display: flex;
    transition: transform 0.3s ease-in-out;
}

.hi-carousel-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.5);
    border: none;
    color: white;
    font-size: 24px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    z-index: 10;
}

.hi-carousel-arrow:hover {
    background-color: rgba(0, 0, 0, 0.7);
}

.hi-carousel-arrow:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

.hi-carousel-arrow-prev {
    left: 16px;
}

.hi-carousel-arrow-next {
    right: 16px;
}

.hi-carousel-indicators {
    position: absolute;
    display: flex;
    gap: 8px;
    z-index: 10;
}

.hi-carousel-indicators-dots {
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
}

.hi-carousel-indicators-line {
    bottom: 0;
    left: 0;
    right: 0;
    height: 4px;
    background-color: rgba(0, 0, 0, 0.2);
}

.hi-carousel-indicators-hidden {
    display: none;
}

.hi-carousel-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.5);
    border: none;
    cursor: pointer;
    transition: all 0.2s ease;
}

.hi-carousel-dot:hover {
    background-color: rgba(255, 255, 255, 0.8);
}

.hi-carousel-dot-active {
    background-color: var(--hi-color-primary);
    transform: scale(1.2);
}

.hi-carousel-pause {
    position: absolute;
    bottom: 16px;
    right: 16px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.5);
    border: none;
    color: white;
    font-size: 12px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    z-index: 10;
}

.hi-carousel-pause:hover {
    background-color: rgba(0, 0, 0, 0.7);
}
"#
    }

    fn name() -> &'static str {
        "carousel"
    }
}

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

    #[test]
    fn test_carousel_indicator_position_default() {
        let position = CarouselIndicatorPosition::default();
        assert_eq!(position, CarouselIndicatorPosition::Bottom);
    }

    #[test]
    fn test_carousel_indicator_type_default() {
        let indicator = CarouselIndicatorType::default();
        assert_eq!(indicator, CarouselIndicatorType::Dots);
    }

    #[test]
    fn test_carousel_indicator_position_variants() {
        assert!(matches!(CarouselIndicatorPosition::Bottom, _));
        assert!(matches!(CarouselIndicatorPosition::Top, _));
        assert!(matches!(CarouselIndicatorPosition::Left, _));
        assert!(matches!(CarouselIndicatorPosition::Right, _));
    }

    #[test]
    fn test_carousel_indicator_type_variants() {
        assert!(matches!(CarouselIndicatorType::Dots, _));
        assert!(matches!(CarouselIndicatorType::Line, _));
        assert!(matches!(CarouselIndicatorType::Hidden, _));
    }

    #[test]
    fn test_carousel_props_default() {
        let props = CarouselProps::default();
        assert_eq!(props.autoplay, 5000);
        assert!(props.show_arrows);
        assert_eq!(props.indicator_position, CarouselIndicatorPosition::Bottom);
        assert_eq!(props.indicator_type, CarouselIndicatorType::Dots);
        assert!(props.show_pause);
        assert!(!props.infinite);
        assert!(!props.initial_paused);
    }

    #[test]
    fn test_carousel_props_clone() {
        let props = CarouselProps {
            autoplay: 3000,
            show_arrows: false,
            indicator_position: CarouselIndicatorPosition::Top,
            indicator_type: CarouselIndicatorType::Line,
            show_pause: false,
            infinite: true,
            initial_paused: true,
            children: VNode::empty(),
        };

        let cloned = props.clone();
        assert_eq!(cloned.autoplay, 3000);
        assert!(cloned.infinite);
    }

    #[test]
    fn test_carousel_props_partial_eq() {
        let props1 = CarouselProps {
            autoplay: 5000,
            show_arrows: true,
            indicator_position: CarouselIndicatorPosition::Bottom,
            indicator_type: CarouselIndicatorType::Dots,
            show_pause: true,
            infinite: false,
            initial_paused: false,
            children: VNode::empty(),
        };

        let props2 = CarouselProps {
            autoplay: 5000,
            show_arrows: true,
            indicator_position: CarouselIndicatorPosition::Bottom,
            indicator_type: CarouselIndicatorType::Dots,
            show_pause: true,
            infinite: false,
            initial_paused: false,
            children: VNode::empty(),
        };

        assert_eq!(props1, props2);
    }

    #[test]
    fn test_carousel_props_not_equal() {
        let props1 = CarouselProps {
            autoplay: 1000,
            show_arrows: false,
            ..Default::default()
        };

        let props2 = CarouselProps {
            autoplay: 2000,
            show_arrows: false,
            ..Default::default()
        };

        assert_ne!(props1, props2);
    }
}
