use ruin_ui::Element;

use crate::Component;
use crate::input::EventBindings;

#[derive(Clone, Default)]
pub struct View {
    pub(crate) element: Element,
    pub(crate) bindings: EventBindings,
}

impl View {
    pub fn from_element(element: Element) -> Self {
        Self {
            element,
            bindings: EventBindings::default(),
        }
    }

    pub fn element(&self) -> &Element {
        &self.element
    }

    pub fn from_container(element: Element, children: Vec<View>) -> Self {
        let mut composed = Self::from_element(element);
        let mut element = composed.element;

        for child in children {
            element = element.child(child.element);
            composed.bindings.extend(child.bindings);
        }

        composed.element = element;
        composed
    }
}

impl From<Element> for View {
    fn from(element: Element) -> Self {
        Self::from_element(element)
    }
}

pub trait IntoView {
    fn into_view(self) -> View;
}

impl IntoView for View {
    fn into_view(self) -> View {
        self
    }
}

impl IntoView for Element {
    fn into_view(self) -> View {
        View::from_element(self)
    }
}

impl<T: Component> IntoView for T {
    fn into_view(self) -> View {
        self.render()
    }
}

pub trait Children {
    fn into_views(self) -> Vec<View>;
}

#[derive(Clone, Default)]
pub struct ChildViews(pub(crate) Vec<View>);

impl ChildViews {
    pub fn from_children(children: impl Children) -> Self {
        Self(children.into_views())
    }

    pub fn into_vec(self) -> Vec<View> {
        self.0
    }
}

impl Children for () {
    fn into_views(self) -> Vec<View> {
        Vec::new()
    }
}

impl<T: IntoView> Children for T {
    fn into_views(self) -> Vec<View> {
        vec![self.into_view()]
    }
}

impl<T: IntoView> Children for Vec<T> {
    fn into_views(self) -> Vec<View> {
        self.into_iter().map(IntoView::into_view).collect()
    }
}

impl Children for ChildViews {
    fn into_views(self) -> Vec<View> {
        self.0
    }
}

/// Wraps a lazy closure so it can be used as container children.
///
/// The closure is called exactly once, inside the builder's `.children()` method,
/// after any `PartialTextStyle` context has been pushed. This enables text style
/// inheritance from ancestor containers.
///
/// The `view!` macro automatically generates `LazyChildren(|| vec![...])` for
/// primitive container nodes so that style context propagates correctly.
pub struct LazyChildren<F: FnOnce() -> Vec<View>>(pub F);

impl<F: FnOnce() -> Vec<View>> Children for LazyChildren<F> {
    fn into_views(self) -> Vec<View> {
        (self.0)()
    }
}

macro_rules! impl_children_tuple {
    ($($name:ident),+ $(,)?) => {
        #[allow(non_camel_case_types)]
        impl<$($name: IntoView),+> Children for ($($name,)+) {
            fn into_views(self) -> Vec<View> {
                let ($($name,)+) = self;
                vec![$($name.into_view(),)+]
            }
        }
    };
}

impl_children_tuple!(a, b);
impl_children_tuple!(a, b, c);
impl_children_tuple!(a, b, c, d);
impl_children_tuple!(a, b, c, d, e);
impl_children_tuple!(a, b, c, d, e, f);
impl_children_tuple!(a, b, c, d, e, f, g);
impl_children_tuple!(a, b, c, d, e, f, g, h);

pub trait TextChildren {
    fn into_text(self) -> String;
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TextValue(pub(crate) String);

impl TextValue {
    pub fn from_text(children: impl TextChildren) -> Self {
        Self(children.into_text())
    }

    pub fn into_string(self) -> String {
        self.0
    }
}

impl TextChildren for TextValue {
    fn into_text(self) -> String {
        self.0
    }
}

impl TextChildren for &'static str {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl TextChildren for String {
    fn into_text(self) -> String {
        self
    }
}

impl TextChildren for i32 {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl TextChildren for i64 {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl TextChildren for usize {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl TextChildren for u64 {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl TextChildren for f32 {
    fn into_text(self) -> String {
        self.to_string()
    }
}

impl<T: std::fmt::Display + 'static> TextChildren for crate::hooks::Signal<T> {
    fn into_text(self) -> String {
        self.with(|value| value.to_string())
    }
}

impl<T: std::fmt::Display + 'static> TextChildren for crate::hooks::Memo<T> {
    fn into_text(self) -> String {
        self.with(|value| value.to_string())
    }
}

macro_rules! impl_text_children_tuple {
    ($($name:ident),+ $(,)?) => {
        #[allow(non_camel_case_types)]
        impl<$($name: TextChildren),+> TextChildren for ($($name,)+) {
            fn into_text(self) -> String {
                let ($($name,)+) = self;
                let mut text = String::new();
                $(text.push_str(&$name.into_text());)+
                text
            }
        }
    };
}

impl_text_children_tuple!(a, b);
impl_text_children_tuple!(a, b, c);
impl_text_children_tuple!(a, b, c, d);
impl_text_children_tuple!(a, b, c, d, e);
impl_text_children_tuple!(a, b, c, d, e, f);
