use ruin_ui::{AlignItems, Color, Element, ElementId, JustifyContent};

use crate::context::allocate_element_id;
use crate::converters::{IntoBorder, IntoEdges, IntoShadow};
use crate::hooks::{Signal, WidgetRef};
use crate::text_style::{PartialTextStyle, pop_text_style, push_text_style};
use crate::view::{Children, View};

/// Shared box-model properties common to all container-like builders.
pub struct ContainerProps {
    pub(crate) element: Element,
    pub(crate) widget_ref: Option<Signal<Option<ElementId>>>,
    pub(crate) partial_text_style: Option<PartialTextStyle>,
}

impl ContainerProps {
    pub(crate) fn new(element: Element) -> Self {
        Self {
            element,
            widget_ref: None,
            partial_text_style: None,
        }
    }

    pub fn text_style(mut self, style: PartialTextStyle) -> Self {
        self.partial_text_style = Some(style);
        self
    }

    pub fn gap(mut self, gap: f32) -> Self {
        self.element = self.element.gap(gap);
        self
    }

    pub fn padding(mut self, padding: impl IntoEdges) -> Self {
        self.element = self.element.padding(padding.into_edges());
        self
    }

    pub fn background(mut self, color: Color) -> Self {
        self.element = self.element.background(color);
        self
    }

    pub fn border(mut self, border: impl IntoBorder) -> Self {
        let border = border.into_border();
        self.element = self.element.border(border.width, border.color);
        self
    }

    pub fn border_radius(mut self, radius: f32) -> Self {
        self.element = self.element.corner_radius(radius);
        self
    }

    pub fn width(mut self, width: f32) -> Self {
        self.element = self.element.width(width);
        self
    }

    pub fn height(mut self, height: f32) -> Self {
        self.element = self.element.height(height);
        self
    }

    pub fn min_width(mut self, min_width: f32) -> Self {
        self.element = self.element.min_width(min_width);
        self
    }

    pub fn min_height(mut self, min_height: f32) -> Self {
        self.element = self.element.min_height(min_height);
        self
    }

    pub fn min_size(self, w: f32, h: f32) -> Self {
        self.min_width(w).min_height(h)
    }

    pub fn flex(mut self, flex: f32) -> Self {
        self.element = self.element.flex(flex);
        self
    }

    pub fn align_items(mut self, align: AlignItems) -> Self {
        self.element = self.element.align_items(align);
        self
    }

    pub fn align_self(mut self, align: AlignItems) -> Self {
        self.element = self.element.align_self(align);
        self
    }

    pub fn justify_content(mut self, justify: JustifyContent) -> Self {
        self.element = self.element.justify_content(justify);
        self
    }

    pub fn shadow(mut self, shadow: impl IntoShadow) -> Self {
        self.element = self.element.shadow(shadow.into_shadow());
        self
    }

    pub fn widget_ref<T>(mut self, widget_ref: WidgetRef<T>) -> Self {
        self.widget_ref = Some(widget_ref.element_id.clone());
        self
    }
}

pub struct ContainerBuilder {
    pub(crate) props: ContainerProps,
}

pub fn column() -> ContainerBuilder {
    ContainerBuilder {
        props: ContainerProps::new(Element::column()),
    }
}

pub fn row() -> ContainerBuilder {
    ContainerBuilder {
        props: ContainerProps::new(Element::row()),
    }
}

pub fn block() -> ContainerBuilder {
    ContainerBuilder {
        props: ContainerProps::new(Element::column()),
    }
}

impl ContainerBuilder {
    impl_props_methods!();

    pub fn shadow(mut self, shadow: impl IntoShadow) -> Self {
        self.props = self.props.shadow(shadow);
        self
    }

    pub fn text_style(mut self, style: PartialTextStyle) -> Self {
        self.props = self.props.text_style(style);
        self
    }

    pub fn children(mut self, children: impl Children) -> View {
        if let Some(widget_ref) = &self.props.widget_ref {
            let element_id = allocate_element_id();
            self.props.element = self.props.element.id(element_id);
            let _ = widget_ref.set(Some(element_id));
        }
        let had_style = self.props.partial_text_style.is_some();
        if let Some(style) = self.props.partial_text_style.take() {
            push_text_style(style);
        }
        let children_views = children.into_views();
        if had_style {
            pop_text_style();
        }
        View::from_container(self.props.element, children_views)
    }
}
