use crate::id::Id;
use derive_rich::Rich;
use savory::prelude::*;
use savory_style::{box_align::*, flexbox::*, prelude::*, values as val};

#[derive(Rich)]
pub struct Item<Msg> {
    #[rich(write)]
    pub id: Option<Id>,

    #[rich(write(style = compose))]
    pub content: Node<Msg>,
    #[rich(write)]
    pub order: Option<Order>,
    #[rich(write)]
    pub grow: Option<Grow>,
    #[rich(write)]
    pub shrink: Option<Shrink>,
    #[rich(write)]
    pub basis: Option<Basis>,
    #[rich(value_fns = {
        auto = val::Auto,
        normal = val::Normal,
        stretch = val::Stretch,
        center = val::Center,
        start = val::Start,
        end = val::End,
    })]
    pub align_self: Option<AlignSelf>,
    #[rich(read(copy, rename = is_flatten), value_fns = { flatten = true, wrapped = false })]
    pub flatten: bool,
}

impl<Msg> View<Node<Msg>> for Item<Msg> {
    fn view(&self) -> Node<Msg> {
        let styler = |s: Style| {
            s.try_flex_order(self.order)
                .try_flex_grow(self.grow)
                .try_flex_shrink(self.shrink)
                .try_flex_basis(self.basis.clone())
                .try_align_self(self.align_self)
        };

        if self.is_flatten() {
            self.content.clone().and_style(styler)
        } else {
            html::div()
                .try_id(self.id.clone())
                .class("flex-item")
                .and_style(styler)
                .push(self.content.clone())
        }
    }
}

impl<Msg> Item<Msg> {
    pub fn group(self, group_id: impl Into<Order>) -> Self {
        self.order(group_id)
    }
}

impl<Msg> From<Node<Msg>> for Item<Msg> {
    fn from(node: Node<Msg>) -> Self {
        Self {
            id: None,
            content: node,
            order: None,
            grow: None,
            shrink: None,
            basis: None,
            align_self: None,
            flatten: false,
        }
    }
}

impl<'a, Msg> From<&'a dyn View<Node<Msg>>> for Item<Msg> {
    fn from(source: &'a dyn View<Node<Msg>>) -> Self {
        Self::from(source.view())
    }
}

impl<T, Msg> From<&T> for Item<Msg>
where
    T: View<Node<Msg>>,
{
    fn from(view: &T) -> Self {
        Self::from(view.view())
    }
}
