/// VStack, HStack, ZStack — the primary layout building blocks.
///
/// These are the component API. FlexLayout is the engine underneath.
/// VStack and HStack wrap automatically by default — that's what makes
/// the layout responsive without writing breakpoints.

use crate::flex::{CrossAxisAlignment, FlexLayout, HAlign, VAlign};
use el_style::modifier::{StyleModifier, StyleSet};

/// A vertical stack — children arranged from top to bottom.
///
/// Wraps automatically when height is constrained (mobile-first).
/// The default gap is 8dp. Cross-axis fills the container width.
#[derive(Debug, Clone)]
pub struct VStack {
    /// Space between children in dp (default: 8).
    pub spacing: u32,
    /// Horizontal alignment of children (default: Leading).
    pub alignment: HAlign,
    /// Whether to wrap to a new column when out of vertical space (default: true).
    pub wrap: bool,
    pub style: StyleSet,
}

impl Default for VStack {
    fn default() -> Self {
        Self {
            spacing: 8,
            alignment: HAlign::Leading,
            wrap: true,
            style: StyleSet::default(),
        }
    }
}

impl VStack {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn spacing(mut self, dp: u32) -> Self {
        self.spacing = dp;
        self
    }

    pub fn alignment(mut self, align: HAlign) -> Self {
        self.alignment = align;
        self
    }

    /// Enable or disable wrapping.
    pub fn wrap(mut self, wrap: bool) -> Self {
        self.wrap = wrap;
        self
    }

    /// Convert to FlexLayout spec.
    pub fn to_flex(&self) -> FlexLayout {
        let mut flex = FlexLayout::vstack(self.spacing, self.wrap);
        flex.cross_axis_alignment = match self.alignment {
            HAlign::Leading => CrossAxisAlignment::Start,
            HAlign::Center => CrossAxisAlignment::Center,
            HAlign::Trailing => CrossAxisAlignment::End,
        };
        flex
    }
}

impl StyleModifier for VStack {
    fn style_mut(&mut self) -> &mut StyleSet {
        &mut self.style
    }
}

/// A horizontal stack — children arranged from leading to trailing.
///
/// Respects reading direction (RTL reverses automatically).
/// Wraps to new rows by default when width is limited (mobile-first).
#[derive(Debug, Clone)]
pub struct HStack {
    /// Space between children in dp (default: 8).
    pub spacing: u32,
    /// Vertical alignment of children (default: Center).
    pub alignment: VAlign,
    /// Whether to wrap to a new row when out of horizontal space (default: true).
    pub wrap: bool,
    pub style: StyleSet,
}

impl Default for HStack {
    fn default() -> Self {
        Self {
            spacing: 8,
            alignment: VAlign::Center,
            wrap: true,
            style: StyleSet::default(),
        }
    }
}

impl HStack {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn spacing(mut self, dp: u32) -> Self {
        self.spacing = dp;
        self
    }

    pub fn alignment(mut self, align: VAlign) -> Self {
        self.alignment = align;
        self
    }

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

    /// Convert to FlexLayout spec.
    pub fn to_flex(&self) -> FlexLayout {
        let mut flex = FlexLayout::hstack(self.spacing, self.wrap);
        flex.cross_axis_alignment = match self.alignment {
            VAlign::Top => CrossAxisAlignment::Start,
            VAlign::Center => CrossAxisAlignment::Center,
            VAlign::Bottom => CrossAxisAlignment::End,
            VAlign::Baseline => CrossAxisAlignment::Baseline,
        };
        flex
    }
}

impl StyleModifier for HStack {
    fn style_mut(&mut self) -> &mut StyleSet {
        &mut self.style
    }
}

/// A depth stack — children layered on top of each other.
///
/// ZStack places all children at the same position, overlapping.
/// Later children appear on top of earlier children.
#[derive(Debug, Clone)]
pub struct ZStack {
    pub h_align: HAlign,
    pub v_align: VAlign,
    pub style: StyleSet,
}

impl Default for ZStack {
    fn default() -> Self {
        Self {
            h_align: HAlign::Center,
            v_align: VAlign::Center,
            style: StyleSet::default(),
        }
    }
}

impl ZStack {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn h_align(mut self, align: HAlign) -> Self {
        self.h_align = align;
        self
    }

    pub fn v_align(mut self, align: VAlign) -> Self {
        self.v_align = align;
        self
    }
}

impl StyleModifier for ZStack {
    fn style_mut(&mut self) -> &mut StyleSet {
        &mut self.style
    }
}

/// A spacer that expands to fill available space in a stack.
///
/// In HStack: expands horizontally. In VStack: expands vertically.
#[derive(Debug, Clone, Default)]
pub struct Spacer {
    /// Minimum size in dp (0 = truly flexible).
    pub min_size: u32,
}

impl Spacer {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn min(mut self, dp: u32) -> Self {
        self.min_size = dp;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::flex::CrossAxisAlignment;
    use el_style::modifier::StyleModifier;
    use el_style::color::Color;

    #[test]
    fn vstack_default_spacing() {
        let v = VStack::new();
        assert_eq!(v.spacing, 8);
    }

    #[test]
    fn vstack_default_wraps() {
        let v = VStack::new();
        assert!(v.wrap);
    }

    #[test]
    fn vstack_to_flex_direction() {
        let v = VStack::new();
        let flex = v.to_flex();
        assert!(flex.direction.is_vertical());
    }

    #[test]
    fn vstack_center_alignment() {
        let v = VStack::new().alignment(HAlign::Center);
        let flex = v.to_flex();
        assert_eq!(flex.cross_axis_alignment, CrossAxisAlignment::Center);
    }

    #[test]
    fn hstack_default_alignment() {
        let h = HStack::new();
        assert_eq!(h.alignment, VAlign::Center);
    }

    #[test]
    fn hstack_default_wraps() {
        let h = HStack::new();
        assert!(h.wrap);
    }

    #[test]
    fn hstack_to_flex_direction() {
        let h = HStack::new();
        let flex = h.to_flex();
        assert!(flex.direction.is_horizontal());
    }

    #[test]
    fn hstack_no_wrap() {
        let h = HStack::new().wrap(false);
        let flex = h.to_flex();
        assert!(!flex.direction.wraps());
    }

    #[test]
    fn zstack_defaults() {
        let z = ZStack::new();
        assert_eq!(z.h_align, HAlign::Center);
        assert_eq!(z.v_align, VAlign::Center);
    }

    #[test]
    fn vstack_style_modifier() {
        let v = VStack::new().background(Color::Surface);
        assert_eq!(v.style.background, Some(Color::Surface));
    }

    #[test]
    fn spacer_min_size() {
        let s = Spacer::new().min(16);
        assert_eq!(s.min_size, 16);
    }

    #[test]
    fn spacer_default_zero() {
        let s = Spacer::new();
        assert_eq!(s.min_size, 0);
    }
}
