use dioxus_core_types::DioxusFormattable;

use crate::innerlude::VProps;
use crate::prelude::RenderError;
use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
use crate::{arena::ElementId, Element, Event};
use crate::{
    innerlude::{ElementRef, EventHandler, MountId},
    properties::ComponentFunction,
};
use crate::{Properties, ScopeId, VirtualDom};
use std::ops::Deref;
use std::rc::Rc;
use std::vec;
use std::{
    any::{Any, TypeId},
    cell::Cell,
    fmt::{Arguments, Debug},
};

/// The information about the
#[derive(Debug)]
pub(crate) struct VNodeMount {
    /// The parent of this node
    pub parent: Option<ElementRef>,

    /// A back link to the original node
    pub node: VNode,

    /// The IDs for the roots of this template - to be used when moving the template around and removing it from
    /// the actual Dom
    pub root_ids: Box<[ElementId]>,

    /// The element in the DOM that each attribute is mounted to
    pub(crate) mounted_attributes: Box<[ElementId]>,

    /// For components: This is the ScopeId the component is mounted to
    /// For other dynamic nodes: This is element in the DOM that each dynamic node is mounted to
    pub(crate) mounted_dynamic_nodes: Box<[usize]>,
}

/// A reference to a template along with any context needed to hydrate it
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template.
#[derive(Debug)]
pub struct VNodeInner {
    /// The key given to the root of this template.
    ///
    /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
    pub key: Option<String>,

    /// The static nodes and static descriptor of the template
    pub template: Template,

    /// The dynamic nodes in the template
    pub dynamic_nodes: Box<[DynamicNode]>,

    /// The dynamic attribute slots in the template
    ///
    /// This is a list of positions in the template where dynamic attributes can be inserted.
    ///
    /// The inner list *must* be in the format [static named attributes, remaining dynamically named attributes].
    ///
    /// For example:
    /// ```rust
    /// # use dioxus::prelude::*;
    /// let class = "my-class";
    /// let attrs = vec![];
    /// let color = "red";
    ///
    /// rsx! {
    ///     div {
    ///         class: "{class}",
    ///         ..attrs,
    ///         p {
    ///             color: "{color}",
    ///         }
    ///     }
    /// };
    /// ```
    ///
    /// Would be represented as:
    /// ```text
    /// [
    ///     [class, every attribute in attrs sorted by name], // Slot 0 in the template
    ///     [color], // Slot 1 in the template
    /// ]
    /// ```
    pub dynamic_attrs: Box<[Box<[Attribute]>]>,
}

/// A reference to a template along with any context needed to hydrate it
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template.
#[derive(Debug, Clone)]
pub struct VNode {
    vnode: Rc<VNodeInner>,

    /// The mount information for this template
    pub(crate) mount: Cell<MountId>,
}

impl AsRef<VNode> for Element {
    fn as_ref(&self) -> &VNode {
        match self {
            Element::Ok(node) => node,
            Element::Err(RenderError::Aborted(err)) => &err.render,
            Element::Err(RenderError::Suspended(fut)) => &fut.placeholder,
        }
    }
}

impl From<&Element> for VNode {
    fn from(val: &Element) -> Self {
        AsRef::as_ref(val).clone()
    }
}

impl From<Element> for VNode {
    fn from(val: Element) -> Self {
        match val {
            Element::Ok(node) => node,
            Element::Err(RenderError::Aborted(err)) => err.render,
            Element::Err(RenderError::Suspended(fut)) => fut.placeholder,
        }
    }
}

/// A tiny helper trait to get the vnode for a Element
pub(crate) trait AsVNode {
    /// Get the vnode for this element
    fn as_vnode(&self) -> &VNode;

    /// Create a deep clone of this VNode
    fn deep_clone(&self) -> Self;
}

impl AsVNode for Element {
    fn as_vnode(&self) -> &VNode {
        AsRef::as_ref(self)
    }

    fn deep_clone(&self) -> Self {
        match self {
            Ok(node) => Ok(node.deep_clone()),
            Err(RenderError::Aborted(err)) => Err(RenderError::Aborted(err.deep_clone())),
            Err(RenderError::Suspended(fut)) => Err(RenderError::Suspended(fut.deep_clone())),
        }
    }
}

impl Default for VNode {
    fn default() -> Self {
        Self::placeholder()
    }
}

impl Drop for VNode {
    fn drop(&mut self) {
        // FIXME:
        // TODO:
        //
        // We have to add this drop *here* because we can't add a drop impl to AttributeValue and
        // keep semver compatibility. Adding a drop impl means you can't destructure the value, which
        // we need to do for enums.
        //
        // if dropping this will drop the last vnode (rc count is 1), then we need to drop the listeners
        // in this template
        if Rc::strong_count(&self.vnode) == 1 {
            for attrs in self.vnode.dynamic_attrs.iter() {
                for attr in attrs.iter() {
                    if let AttributeValue::Listener(listener) = &attr.value {
                        listener.callback.manually_drop();
                    }
                }
            }
        }
    }
}

impl PartialEq for VNode {
    fn eq(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.vnode, &other.vnode)
    }
}

impl Deref for VNode {
    type Target = VNodeInner;

    fn deref(&self) -> &Self::Target {
        &self.vnode
    }
}

impl VNode {
    /// Create a template with no nodes that will be skipped over during diffing
    pub fn empty() -> Element {
        Ok(Self::default())
    }

    /// Create a template with a single placeholder node
    pub fn placeholder() -> Self {
        use std::cell::OnceCell;
        // We can reuse all placeholders across the same thread to save memory
        thread_local! {
            static PLACEHOLDER_VNODE: OnceCell<Rc<VNodeInner>> = const { OnceCell::new() };
        }
        let vnode = PLACEHOLDER_VNODE.with(|cell| {
            cell.get_or_init(move || {
                Rc::new(VNodeInner {
                    key: None,
                    dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
                    dynamic_attrs: Box::new([]),
                    template: Template {
                        roots: &[TemplateNode::Dynamic { id: 0 }],
                        node_paths: &[&[0]],
                        attr_paths: &[],
                    },
                })
            })
            .clone()
        });
        Self {
            vnode,
            mount: Default::default(),
        }
    }

    /// Create a new VNode
    pub fn new(
        key: Option<String>,
        template: Template,
        dynamic_nodes: Box<[DynamicNode]>,
        dynamic_attrs: Box<[Box<[Attribute]>]>,
    ) -> Self {
        Self {
            vnode: Rc::new(VNodeInner {
                key,
                template,
                dynamic_nodes,
                dynamic_attrs,
            }),
            mount: Default::default(),
        }
    }

    /// Load a dynamic root at the given index
    ///
    /// Returns [`None`] if the root is actually a static node (Element/Text)
    pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
        self.template.roots[idx]
            .dynamic_id()
            .map(|id| &self.dynamic_nodes[id])
    }

    /// Get the mounted id for a dynamic node index
    pub fn mounted_dynamic_node(
        &self,
        dynamic_node_idx: usize,
        dom: &VirtualDom,
    ) -> Option<ElementId> {
        let mount = self.mount.get().as_usize()?;

        match &self.dynamic_nodes[dynamic_node_idx] {
            DynamicNode::Text(_) | DynamicNode::Placeholder(_) => {
                let mounts = dom.runtime.mounts.borrow();
                mounts
                    .get(mount)?
                    .mounted_dynamic_nodes
                    .get(dynamic_node_idx)
                    .map(|id| ElementId(*id))
            }
            _ => None,
        }
    }

    /// Get the mounted id for a root node index
    pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
        let mount = self.mount.get().as_usize()?;

        let mounts = dom.runtime.mounts.borrow();
        mounts.get(mount)?.root_ids.get(root_idx).copied()
    }

    /// Get the mounted id for a dynamic attribute index
    pub fn mounted_dynamic_attribute(
        &self,
        dynamic_attribute_idx: usize,
        dom: &VirtualDom,
    ) -> Option<ElementId> {
        let mount = self.mount.get().as_usize()?;

        let mounts = dom.runtime.mounts.borrow();
        mounts
            .get(mount)?
            .mounted_attributes
            .get(dynamic_attribute_idx)
            .copied()
    }

    /// Create a deep clone of this VNode
    pub(crate) fn deep_clone(&self) -> Self {
        Self {
            vnode: Rc::new(VNodeInner {
                key: self.vnode.key.clone(),
                template: self.vnode.template,
                dynamic_nodes: self
                    .vnode
                    .dynamic_nodes
                    .iter()
                    .map(|node| match node {
                        DynamicNode::Fragment(nodes) => DynamicNode::Fragment(
                            nodes.iter().map(|node| node.deep_clone()).collect(),
                        ),
                        other => other.clone(),
                    })
                    .collect(),
                dynamic_attrs: self
                    .vnode
                    .dynamic_attrs
                    .iter()
                    .map(|attr| {
                        attr.iter()
                            .map(|attribute| attribute.deep_clone())
                            .collect()
                    })
                    .collect(),
            }),
            mount: Default::default(),
        }
    }
}

type StaticStr = &'static str;
type StaticPathArray = &'static [&'static [u8]];
type StaticTemplateArray = &'static [TemplateNode];
type StaticTemplateAttributeArray = &'static [TemplateAttribute];

/// A static layout of a UI tree that describes a set of dynamic and static nodes.
///
/// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
/// dynamic node. This struct can be created at compile time. It promises that its pointer is unique, allow Dioxus to use
/// its static description of the UI to skip immediately to the dynamic nodes during diffing.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)]
pub struct Template {
    /// The list of template nodes that make up the template
    ///
    /// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
    #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
    pub roots: StaticTemplateArray,

    /// The paths of each node relative to the root of the template.
    ///
    /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
    /// topmost element, not the `roots` field.
    #[cfg_attr(
        feature = "serialize",
        serde(deserialize_with = "deserialize_bytes_leaky")
    )]
    pub node_paths: StaticPathArray,

    /// The paths of each dynamic attribute relative to the root of the template
    ///
    /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
    /// topmost element, not the `roots` field.
    #[cfg_attr(
        feature = "serialize",
        serde(deserialize_with = "deserialize_bytes_leaky", bound = "")
    )]
    pub attr_paths: StaticPathArray,
}

// Are identical static items merged in the current build. Rust doesn't have a cfg(merge_statics) attribute
// so we have to check this manually
#[allow(unpredictable_function_pointer_comparisons)] // This attribute should be removed once MSRV is 1.85 or greater and the below change is made
fn static_items_merged() -> bool {
    fn a() {}
    fn b() {}
    a as fn() == b as fn()
    // std::ptr::fn_addr_eq(a as fn(), b as fn()) <<<<---- This should replace the a as fn() === b as fn() once the MSRV is 1.85 or greater
}

impl std::hash::Hash for Template {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        // If identical static items are merged, we can compare templates by pointer
        if static_items_merged() {
            std::ptr::hash(self.roots as *const _, state);
            std::ptr::hash(self.node_paths as *const _, state);
            std::ptr::hash(self.attr_paths as *const _, state);
        }
        // Otherwise, we hash by value
        else {
            self.roots.hash(state);
            self.node_paths.hash(state);
            self.attr_paths.hash(state);
        }
    }
}

impl PartialEq for Template {
    fn eq(&self, other: &Self) -> bool {
        // If identical static items are merged, we can compare templates by pointer
        if static_items_merged() {
            std::ptr::eq(self.roots as *const _, other.roots as *const _)
                && std::ptr::eq(self.node_paths as *const _, other.node_paths as *const _)
                && std::ptr::eq(self.attr_paths as *const _, other.attr_paths as *const _)
        }
        // Otherwise, we compare by value
        else {
            self.roots == other.roots
                && self.node_paths == other.node_paths
                && self.attr_paths == other.attr_paths
        }
    }
}

#[cfg(feature = "serialize")]
pub(crate) fn deserialize_string_leaky<'a, 'de, D>(
    deserializer: D,
) -> Result<&'static str, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;

    let deserialized = String::deserialize(deserializer)?;
    Ok(&*Box::leak(deserialized.into_boxed_str()))
}

#[cfg(feature = "serialize")]
fn deserialize_bytes_leaky<'a, 'de, D>(
    deserializer: D,
) -> Result<&'static [&'static [u8]], D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;

    let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
    let deserialized = deserialized
        .into_iter()
        .map(|v| &*Box::leak(v.into_boxed_slice()))
        .collect::<Vec<_>>();
    Ok(&*Box::leak(deserialized.into_boxed_slice()))
}

#[cfg(feature = "serialize")]
pub(crate) fn deserialize_leaky<'a, 'de, T, D>(deserializer: D) -> Result<&'static [T], D::Error>
where
    T: serde::Deserialize<'de>,
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;

    let deserialized = Box::<[T]>::deserialize(deserializer)?;
    Ok(&*Box::leak(deserialized))
}

#[cfg(feature = "serialize")]
pub(crate) fn deserialize_option_leaky<'a, 'de, D>(
    deserializer: D,
) -> Result<Option<&'static str>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;

    let deserialized = Option::<String>::deserialize(deserializer)?;
    Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
}

impl Template {
    /// Is this template worth caching at all, since it's completely runtime?
    ///
    /// There's no point in saving templates that are completely dynamic, since they'll be recreated every time anyway.
    pub fn is_completely_dynamic(&self) -> bool {
        use TemplateNode::*;
        self.roots.iter().all(|root| matches!(root, Dynamic { .. }))
    }
}

/// A statically known node in a layout.
///
/// This can be created at compile time, saving the VirtualDom time when diffing the tree
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    serde(tag = "type")
)]
pub enum TemplateNode {
    /// An statically known element in the dom.
    ///
    /// In HTML this would be something like `<div id="123"> </div>`
    Element {
        /// The name of the element
        ///
        /// IE for a div, it would be the string "div"
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_string_leaky")
        )]
        tag: StaticStr,

        /// The namespace of the element
        ///
        /// In HTML, this would be a valid URI that defines a namespace for all elements below it
        /// SVG is an example of this namespace
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_option_leaky")
        )]
        namespace: Option<StaticStr>,

        /// A list of possibly dynamic attributes for this element
        ///
        /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_leaky", bound = "")
        )]
        attrs: StaticTemplateAttributeArray,

        /// A list of template nodes that define another set of template nodes
        #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
        children: StaticTemplateArray,
    },

    /// This template node is just a piece of static text
    Text {
        /// The actual text
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_string_leaky", bound = "")
        )]
        text: StaticStr,
    },

    /// This template node is unknown, and needs to be created at runtime.
    Dynamic {
        /// The index of the dynamic node in the VNode's dynamic_nodes list
        id: usize,
    },
}

impl TemplateNode {
    /// Try to load the dynamic node at the given index
    pub fn dynamic_id(&self) -> Option<usize> {
        use TemplateNode::*;
        match self {
            Dynamic { id } => Some(*id),
            _ => None,
        }
    }
}

/// A node created at runtime
///
/// This node's index in the DynamicNode list on VNode should match its respective `Dynamic` index
#[derive(Debug, Clone)]
pub enum DynamicNode {
    /// A component node
    ///
    /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
    /// assigned scope are dynamic.
    ///
    /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
    /// the render function at runtime
    Component(VComponent),

    /// A text node
    Text(VText),

    /// A placeholder
    ///
    /// Used by suspense when a node isn't ready and by fragments that don't render anything
    ///
    /// In code, this is just an ElementId whose initial value is set to 0 upon creation
    Placeholder(VPlaceholder),

    /// A list of VNodes.
    ///
    /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
    /// or iterators.
    Fragment(Vec<VNode>),
}

impl DynamicNode {
    /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
    pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
        into.into_dyn_node()
    }
}

impl Default for DynamicNode {
    fn default() -> Self {
        Self::Placeholder(Default::default())
    }
}

/// An instance of a child component
pub struct VComponent {
    /// The name of this component
    pub name: &'static str,

    /// The raw pointer to the render function
    pub(crate) render_fn: usize,

    /// The props for this component
    pub(crate) props: BoxedAnyProps,
}

impl Clone for VComponent {
    fn clone(&self) -> Self {
        Self {
            name: self.name,
            props: self.props.duplicate(),
            render_fn: self.render_fn,
        }
    }
}

impl VComponent {
    /// Create a new [`VComponent`] variant
    pub fn new<P, M: 'static>(
        component: impl ComponentFunction<P, M>,
        props: P,
        fn_name: &'static str,
    ) -> Self
    where
        P: Properties + 'static,
    {
        let render_fn = component.fn_ptr();
        let props = Box::new(VProps::new(
            component,
            <P as Properties>::memoize,
            props,
            fn_name,
        ));

        VComponent {
            render_fn,
            name: fn_name,
            props,
        }
    }

    /// Get the [`ScopeId`] this node is mounted to if it's mounted
    ///
    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
    ///
    /// Returns [`None`] if the node is not mounted
    pub fn mounted_scope_id(
        &self,
        dynamic_node_index: usize,
        vnode: &VNode,
        dom: &VirtualDom,
    ) -> Option<ScopeId> {
        let mount = vnode.mount.get().as_usize()?;

        let mounts = dom.runtime.mounts.borrow();
        let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];

        Some(ScopeId(scope_id))
    }

    /// Get the scope this node is mounted to if it's mounted
    ///
    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
    ///
    /// Returns [`None`] if the node is not mounted
    pub fn mounted_scope<'a>(
        &self,
        dynamic_node_index: usize,
        vnode: &VNode,
        dom: &'a VirtualDom,
    ) -> Option<&'a ScopeState> {
        let mount = vnode.mount.get().as_usize()?;

        let mounts = dom.runtime.mounts.borrow();
        let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];

        dom.scopes.get(scope_id)
    }
}

impl std::fmt::Debug for VComponent {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("VComponent")
            .field("name", &self.name)
            .finish()
    }
}

/// A text node
#[derive(Clone, Debug)]
pub struct VText {
    /// The actual text itself
    pub value: String,
}

impl VText {
    /// Create a new VText
    pub fn new(value: impl ToString) -> Self {
        Self {
            value: value.to_string(),
        }
    }
}

impl From<Arguments<'_>> for VText {
    fn from(args: Arguments) -> Self {
        Self::new(args.to_string())
    }
}

/// A placeholder node, used by suspense and fragments
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct VPlaceholder {}

/// An attribute of the TemplateNode, created at compile time
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    serde(tag = "type")
)]
pub enum TemplateAttribute {
    /// This attribute is entirely known at compile time, enabling
    Static {
        /// The name of this attribute.
        ///
        /// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_string_leaky", bound = "")
        )]
        name: StaticStr,

        /// The value of this attribute, known at compile time
        ///
        /// Currently this only accepts &str, so values, even if they're known at compile time, are not known
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_string_leaky", bound = "")
        )]
        value: StaticStr,

        /// The namespace of this attribute. Does not exist in the HTML spec
        #[cfg_attr(
            feature = "serialize",
            serde(deserialize_with = "deserialize_option_leaky", bound = "")
        )]
        namespace: Option<StaticStr>,
    },

    /// The attribute in this position is actually determined dynamically at runtime
    ///
    /// This is the index into the dynamic_attributes field on the container VNode
    Dynamic {
        /// The index
        id: usize,
    },
}

/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
    /// The name of the attribute.
    pub name: &'static str,

    /// The value of the attribute
    pub value: AttributeValue,

    /// The namespace of the attribute.
    ///
    /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
    pub namespace: Option<&'static str>,

    /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
    pub volatile: bool,
}

impl Attribute {
    /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
    ///
    /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in
    /// some renderers stay in sync with the VirtualDom's understanding of the world
    pub fn new<T>(
        name: &'static str,
        value: impl IntoAttributeValue<T>,
        namespace: Option<&'static str>,
        volatile: bool,
    ) -> Attribute {
        Attribute {
            name,
            namespace,
            volatile,
            value: value.into_value(),
        }
    }

    /// Create a new deep clone of this attribute
    pub(crate) fn deep_clone(&self) -> Self {
        Attribute {
            name: self.name,
            namespace: self.namespace,
            volatile: self.volatile,
            value: match &self.value {
                AttributeValue::Listener(listener) => {
                    AttributeValue::Listener(listener.leak_reference().unwrap())
                }
                value => value.clone(),
            },
        }
    }
}

/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
///
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
/// variant.
#[derive(Clone)]
pub enum AttributeValue {
    /// Text attribute
    Text(String),

    /// A float
    Float(f64),

    /// Signed integer
    Int(i64),

    /// Boolean
    Bool(bool),

    /// A listener, like "onclick"
    Listener(ListenerCb),

    /// An arbitrary value that implements PartialEq and is static
    Any(Rc<dyn AnyValue>),

    /// A "none" value, resulting in the removal of an attribute from the dom
    None,
}

impl AttributeValue {
    /// Create a new [`AttributeValue`] with the listener variant from a callback
    ///
    /// The callback must be confined to the lifetime of the ScopeState
    pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
        // TODO: maybe don't use the copy-variant of EventHandler here?
        // Maybe, create an Owned variant so we are less likely to run into leaks
        AttributeValue::Listener(EventHandler::leak(move |event: Event<dyn Any>| {
            let data = event.data.downcast::<T>().unwrap();
            callback(Event {
                metadata: event.metadata.clone(),
                data,
            });
        }))
    }

    /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
    pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
        AttributeValue::Any(Rc::new(value))
    }
}

pub type ListenerCb = EventHandler<Event<dyn Any>>;

impl std::fmt::Debug for AttributeValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
            Self::Listener(listener) => f.debug_tuple("Listener").field(listener).finish(),
            Self::Any(_) => f.debug_tuple("Any").finish(),
            Self::None => write!(f, "None"),
        }
    }
}

impl PartialEq for AttributeValue {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Text(l0), Self::Text(r0)) => l0 == r0,
            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
            (Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
            (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
            (Self::None, Self::None) => true,
            _ => false,
        }
    }
}

#[doc(hidden)]
pub trait AnyValue: 'static {
    fn any_cmp(&self, other: &dyn AnyValue) -> bool;
    fn as_any(&self) -> &dyn Any;
    fn type_id(&self) -> TypeId {
        self.as_any().type_id()
    }
}

impl<T: Any + PartialEq + 'static> AnyValue for T {
    fn any_cmp(&self, other: &dyn AnyValue) -> bool {
        if let Some(other) = other.as_any().downcast_ref() {
            self == other
        } else {
            false
        }
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

/// A trait that allows various items to be converted into a dynamic node for the rsx macro
pub trait IntoDynNode<A = ()> {
    /// Consume this item and produce a DynamicNode
    fn into_dyn_node(self) -> DynamicNode;
}

impl IntoDynNode for () {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::default()
    }
}
impl IntoDynNode for VNode {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::Fragment(vec![self])
    }
}
impl IntoDynNode for DynamicNode {
    fn into_dyn_node(self) -> DynamicNode {
        self
    }
}
impl<T: IntoDynNode> IntoDynNode for Option<T> {
    fn into_dyn_node(self) -> DynamicNode {
        match self {
            Some(val) => val.into_dyn_node(),
            None => DynamicNode::default(),
        }
    }
}
impl IntoDynNode for &Element {
    fn into_dyn_node(self) -> DynamicNode {
        match self.as_ref() {
            Ok(val) => val.into_dyn_node(),
            _ => DynamicNode::default(),
        }
    }
}
impl IntoDynNode for Element {
    fn into_dyn_node(self) -> DynamicNode {
        match self {
            Ok(val) => val.into_dyn_node(),
            _ => DynamicNode::default(),
        }
    }
}
impl IntoDynNode for &Option<VNode> {
    fn into_dyn_node(self) -> DynamicNode {
        match self.as_ref() {
            Some(val) => val.clone().into_dyn_node(),
            _ => DynamicNode::default(),
        }
    }
}
impl IntoDynNode for &str {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::Text(VText {
            value: self.to_string(),
        })
    }
}
impl IntoDynNode for String {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::Text(VText { value: self })
    }
}
impl IntoDynNode for Arguments<'_> {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::Text(VText {
            value: self.to_string(),
        })
    }
}
impl IntoDynNode for &VNode {
    fn into_dyn_node(self) -> DynamicNode {
        DynamicNode::Fragment(vec![self.clone()])
    }
}

pub trait IntoVNode {
    fn into_vnode(self) -> VNode;
}
impl IntoVNode for VNode {
    fn into_vnode(self) -> VNode {
        self
    }
}
impl IntoVNode for &VNode {
    fn into_vnode(self) -> VNode {
        self.clone()
    }
}
impl IntoVNode for Element {
    fn into_vnode(self) -> VNode {
        match self {
            Ok(val) => val.into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}
impl IntoVNode for &Element {
    fn into_vnode(self) -> VNode {
        match self {
            Ok(val) => val.into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}
impl IntoVNode for Option<VNode> {
    fn into_vnode(self) -> VNode {
        match self {
            Some(val) => val.into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}
impl IntoVNode for &Option<VNode> {
    fn into_vnode(self) -> VNode {
        match self.as_ref() {
            Some(val) => val.clone().into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}
impl IntoVNode for Option<Element> {
    fn into_vnode(self) -> VNode {
        match self {
            Some(val) => val.into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}
impl IntoVNode for &Option<Element> {
    fn into_vnode(self) -> VNode {
        match self.as_ref() {
            Some(val) => val.clone().into_vnode(),
            _ => VNode::empty().unwrap(),
        }
    }
}

// Note that we're using the E as a generic but this is never crafted anyways.
pub struct FromNodeIterator;
impl<T, I> IntoDynNode<FromNodeIterator> for T
where
    T: Iterator<Item = I>,
    I: IntoVNode,
{
    fn into_dyn_node(self) -> DynamicNode {
        let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();

        if children.is_empty() {
            DynamicNode::default()
        } else {
            DynamicNode::Fragment(children)
        }
    }
}

/// A value that can be converted into an attribute value
pub trait IntoAttributeValue<T = ()> {
    /// Convert into an attribute value
    fn into_value(self) -> AttributeValue;
}

impl IntoAttributeValue for AttributeValue {
    fn into_value(self) -> AttributeValue {
        self
    }
}

impl IntoAttributeValue for &str {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Text(self.to_string())
    }
}

impl IntoAttributeValue for String {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Text(self)
    }
}

impl IntoAttributeValue for f32 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Float(self as _)
    }
}
impl IntoAttributeValue for f64 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Float(self)
    }
}

impl IntoAttributeValue for i8 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for i16 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for i32 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for i64 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self)
    }
}
impl IntoAttributeValue for isize {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for i128 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}

impl IntoAttributeValue for u8 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for u16 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for u32 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for u64 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for usize {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}
impl IntoAttributeValue for u128 {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Int(self as _)
    }
}

impl IntoAttributeValue for bool {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Bool(self)
    }
}

impl IntoAttributeValue for Arguments<'_> {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Text(self.to_string())
    }
}

impl IntoAttributeValue for Rc<dyn AnyValue> {
    fn into_value(self) -> AttributeValue {
        AttributeValue::Any(self)
    }
}

impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
    fn into_value(self) -> AttributeValue {
        match self {
            Some(val) => val.into_value(),
            None => AttributeValue::None,
        }
    }
}

pub struct AnyFmtMarker;
impl<T> IntoAttributeValue<AnyFmtMarker> for T
where
    T: DioxusFormattable,
{
    fn into_value(self) -> AttributeValue {
        AttributeValue::Text(self.format().to_string())
    }
}

/// A trait for anything that has a dynamic list of attributes
pub trait HasAttributes {
    /// Push an attribute onto the list of attributes
    fn push_attribute<T>(
        self,
        name: &'static str,
        ns: Option<&'static str>,
        attr: impl IntoAttributeValue<T>,
        volatile: bool,
    ) -> Self;
}
