/// A type-erased `AnyAttribute`.
pub mod any_attribute;
/// Types for ARIA attributes.
pub mod aria;
/// Types for custom attributes.
pub mod custom;
/// Traits to define global attribute methods on all HTML elements.
pub mod global;
mod key;
pub(crate) mod maybe_next_attr_erasure_macros;
mod value;

use crate::view::{Position, ToTemplate};
pub use key::*;
use maybe_next_attr_erasure_macros::{
    next_attr_combine, next_attr_output_type,
};
use std::{borrow::Cow, fmt::Debug, future::Future};
pub use value::*;

/// Defines an attribute: anything that can modify an element.
pub trait Attribute: NextAttribute + Send {
    /// The minimum length of this attribute in HTML.
    const MIN_LENGTH: usize;

    /// The state that should be retained between building and rebuilding.
    type State;
    /// The type once all async data have loaded.
    type AsyncOutput: Attribute;
    /// An equivalent to this attribute that can be cloned to be shared across elements.
    type Cloneable: Attribute + Clone;
    /// An equivalent to this attribute that can be cloned to be shared across elements, and
    /// captures no references shorter than `'static`.
    type CloneableOwned: Attribute + Clone + 'static;

    /// An approximation of the actual length of this attribute in HTML.
    fn html_len(&self) -> usize;

    /// Renders the attribute to HTML.
    ///
    /// This separates a general buffer for attribute values from the `class` and `style`
    /// attributes, so that multiple classes or styles can be combined, and also allows for an
    /// `inner_html` attribute that sets the child HTML instead of an attribute.
    fn to_html(
        self,
        buf: &mut String,
        class: &mut String,
        style: &mut String,
        inner_html: &mut String,
    );

    /// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
    /// either been rendered on the server, or cloned for a `<template>`.
    fn hydrate<const FROM_SERVER: bool>(
        self,
        el: &crate::renderer::types::Element,
    ) -> Self::State;

    /// Adds this attribute to the element during client-side rendering.
    fn build(self, el: &crate::renderer::types::Element) -> Self::State;

    /// Applies a new value for the attribute.
    fn rebuild(self, state: &mut Self::State);

    /// Converts this attribute into an equivalent that can be cloned.
    fn into_cloneable(self) -> Self::Cloneable;

    /// Converts this attributes into an equivalent that can be cloned and is `'static`.
    fn into_cloneable_owned(self) -> Self::CloneableOwned;

    /// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
    /// reactive types, this can be used to gather data about reactivity or about asynchronous data
    /// that needs to be loaded.
    fn dry_resolve(&mut self);

    /// “Resolves” this into a type that is not waiting for any asynchronous data.
    fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;

    /// Returns a set of attribute keys, associated with this attribute, if any.
    ///
    /// This is only used to manage the removal of type-erased attributes, when needed.
    fn keys(&self) -> Vec<NamedAttributeKey> {
        // TODO: remove default implementation in 0.9, or fix this whole approach
        // by making it easier to remove attributes
        vec![]
    }
}

/// An attribute key can be used to remove an attribute from an element.
pub enum NamedAttributeKey {
    /// An ordinary attribute.
    Attribute(Cow<'static, str>),
    /// A DOM property.
    Property(Cow<'static, str>),
    /// The `inner_html` pseudo-attribute.
    InnerHtml,
}

/// Adds another attribute to this one, returning a new attribute.
///
/// This is typically achieved by creating or extending a tuple of attributes.
pub trait NextAttribute {
    /// The type of the new, combined attribute.
    type Output<NewAttr: Attribute>: Attribute;

    /// Adds a new attribute.
    fn add_any_attr<NewAttr: Attribute>(
        self,
        new_attr: NewAttr,
    ) -> Self::Output<NewAttr>;
}

impl Attribute for () {
    const MIN_LENGTH: usize = 0;

    type State = ();
    type AsyncOutput = ();
    type Cloneable = ();
    type CloneableOwned = ();

    fn html_len(&self) -> usize {
        0
    }

    fn to_html(
        self,
        _buf: &mut String,
        _class: &mut String,
        _style: &mut String,
        _inner_html: &mut String,
    ) {
    }

    fn hydrate<const FROM_SERVER: bool>(
        self,
        _el: &crate::renderer::types::Element,
    ) -> Self::State {
    }

    fn build(self, _el: &crate::renderer::types::Element) -> Self::State {}

    fn rebuild(self, _state: &mut Self::State) {}

    fn into_cloneable(self) -> Self::Cloneable {
        self
    }

    fn into_cloneable_owned(self) -> Self::Cloneable {
        self
    }

    fn dry_resolve(&mut self) {}

    async fn resolve(self) -> Self::AsyncOutput {}

    fn keys(&self) -> Vec<NamedAttributeKey> {
        vec![]
    }
}

impl NextAttribute for () {
    #[cfg(not(erase_components))]
    type Output<NewAttr: Attribute> = (NewAttr,);

    #[cfg(erase_components)]
    type Output<NewAttr: Attribute> =
        Vec<crate::html::attribute::any_attribute::AnyAttribute>;

    fn add_any_attr<NewAttr: Attribute>(
        self,
        new_attr: NewAttr,
    ) -> Self::Output<NewAttr> {
        #[cfg(not(erase_components))]
        {
            (new_attr,)
        }
        #[cfg(erase_components)]
        {
            use crate::html::attribute::any_attribute::IntoAnyAttribute;

            vec![new_attr.into_any_attr()]
        }
    }
}

/// An attribute with a key and value.
#[derive(Debug)]
pub struct Attr<K, V>(pub K, pub V)
where
    K: AttributeKey,
    V: AttributeValue;

impl<K, V> Clone for Attr<K, V>
where
    K: AttributeKey,
    V: AttributeValue + Clone,
{
    fn clone(&self) -> Self {
        Self(self.0.clone(), self.1.clone())
    }
}

impl<K, V> ToTemplate for Attr<K, V>
where
    K: AttributeKey,
    V: AttributeValue,
{
    fn to_template(
        buf: &mut String,
        _class: &mut String,
        _style: &mut String,
        _inner_html: &mut String,
        _position: &mut Position,
    ) {
        V::to_template(K::KEY, buf);
    }
}

impl<K, V> Attribute for Attr<K, V>
where
    K: AttributeKey + Send,
    V: AttributeValue + Send,
{
    const MIN_LENGTH: usize = 0;

    type State = V::State;
    type AsyncOutput = Attr<K, V::AsyncOutput>;
    type Cloneable = Attr<K, V::Cloneable>;
    type CloneableOwned = Attr<K, V::CloneableOwned>;

    fn html_len(&self) -> usize {
        K::KEY.len() + 3 + self.1.html_len()
    }

    fn to_html(
        self,
        buf: &mut String,
        _class: &mut String,
        _style: &mut String,
        _inner_html: &mut String,
    ) {
        self.1.to_html(K::KEY, buf);
    }

    fn hydrate<const FROM_SERVER: bool>(
        self,
        el: &crate::renderer::types::Element,
    ) -> Self::State {
        self.1.hydrate::<FROM_SERVER>(K::KEY, el)
    }

    fn build(self, el: &crate::renderer::types::Element) -> Self::State {
        V::build(self.1, el, K::KEY)
    }

    fn rebuild(self, state: &mut Self::State) {
        V::rebuild(self.1, K::KEY, state);
    }

    fn into_cloneable(self) -> Self::Cloneable {
        Attr(self.0, self.1.into_cloneable())
    }

    fn into_cloneable_owned(self) -> Self::CloneableOwned {
        Attr(self.0, self.1.into_cloneable_owned())
    }

    fn dry_resolve(&mut self) {
        self.1.dry_resolve();
    }

    async fn resolve(self) -> Self::AsyncOutput {
        Attr(self.0, self.1.resolve().await)
    }

    fn keys(&self) -> Vec<NamedAttributeKey> {
        vec![NamedAttributeKey::Attribute(K::KEY.into())]
    }
}

impl<K, V> NextAttribute for Attr<K, V>
where
    K: AttributeKey,
    V: AttributeValue,
{
    next_attr_output_type!(Self, NewAttr);

    fn add_any_attr<NewAttr: Attribute>(
        self,
        new_attr: NewAttr,
    ) -> Self::Output<NewAttr> {
        next_attr_combine!(self, new_attr)
    }
}

macro_rules! impl_attr_for_tuples {
    ($first:ident, $($ty:ident),* $(,)?) => {
        impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
        where
            $first: Attribute,
            $($ty: Attribute),*,
        {
            const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;

            type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
            type State = ($first::State, $($ty::State,)*);
            type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);
            type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);

            fn html_len(&self) -> usize {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                $first.html_len() $(+ $ty.html_len())*
            }

            fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {
                #[allow(non_snake_case)]
                    let ($first, $($ty,)* ) = self;
                    $first.to_html(buf, class, style, inner_html);
                    $($ty.to_html(buf, class, style, inner_html));*
            }

            fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
                #[allow(non_snake_case)]
                    let ($first, $($ty,)* ) = self;
                    (
                        $first.hydrate::<FROM_SERVER>(el),
                        $($ty.hydrate::<FROM_SERVER>(el)),*
                    )
            }

            fn build(self, el: &crate::renderer::types::Element) -> Self::State {
                #[allow(non_snake_case)]
                    let ($first, $($ty,)*) = self;
                    (
                        $first.build(el),
                        $($ty.build(el)),*
                    )
            }

            fn rebuild(self, state: &mut Self::State) {
                paste::paste! {
                    let ([<$first:lower>], $([<$ty:lower>],)*) = self;
                    let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
                    [<$first:lower>].rebuild([<view_ $first:lower>]);
                    $([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
                }
            }

            fn into_cloneable(self) -> Self::Cloneable {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                (
                    $first.into_cloneable(),
                    $($ty.into_cloneable()),*
                )
            }

            fn into_cloneable_owned(self) -> Self::CloneableOwned {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                (
                    $first.into_cloneable_owned(),
                    $($ty.into_cloneable_owned()),*
                )
            }

            fn dry_resolve(&mut self) {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                $first.dry_resolve();
                $($ty.dry_resolve());*
            }

            async fn resolve(self) -> Self::AsyncOutput {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                futures::join!(
                    $first.resolve(),
                    $($ty.resolve()),*
                )
            }

            fn keys(&self) -> Vec<NamedAttributeKey> {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = &self;
                let mut buf = $first.keys();
                $(buf.extend($ty.keys());)*
                buf
            }
        }

        impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
        where
            $first: Attribute,
            $($ty: Attribute),*,

        {
            type Output<NewAttr: Attribute> = ($first, $($ty,)* NewAttr);

            fn add_any_attr<NewAttr: Attribute>(
                self,
                new_attr: NewAttr,
            ) -> Self::Output<NewAttr> {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                ($first, $($ty,)* new_attr)
            }
        }
    };
}

macro_rules! impl_attr_for_tuples_truncate_additional {
    ($first:ident, $($ty:ident),* $(,)?) => {
        impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
        where
            $first: Attribute,
            $($ty: Attribute),*,
        {
            const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;

            type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
            type State = ($first::State, $($ty::State,)*);
            type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);
            type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);

            fn html_len(&self) -> usize {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                $first.html_len() $(+ $ty.html_len())*
            }

            fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {
                #[allow(non_snake_case)]
                let ($first, $($ty,)* ) = self;
                $first.to_html(buf, class, style, inner_html);
                $($ty.to_html(buf, class, style, inner_html));*
            }

            fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
                #[allow(non_snake_case)]
                let ($first, $($ty,)* ) = self;
                (
                    $first.hydrate::<FROM_SERVER>(el),
                    $($ty.hydrate::<FROM_SERVER>(el)),*
                )
            }

            fn build(self, el: &crate::renderer::types::Element) -> Self::State {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                (
                    $first.build(el),
                    $($ty.build(el)),*
                )
            }

            fn rebuild(self, state: &mut Self::State) {
                paste::paste! {
                    let ([<$first:lower>], $([<$ty:lower>],)*) = self;
                    let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
                    [<$first:lower>].rebuild([<view_ $first:lower>]);
                    $([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
                }
            }

            fn into_cloneable(self) -> Self::Cloneable {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                (
                    $first.into_cloneable(),
                    $($ty.into_cloneable()),*
                )
            }

            fn into_cloneable_owned(self) -> Self::CloneableOwned {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                (
                    $first.into_cloneable_owned(),
                    $($ty.into_cloneable_owned()),*
                )
            }

            fn dry_resolve(&mut self) {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                $first.dry_resolve();
                $($ty.dry_resolve());*
            }

            async fn resolve(self) -> Self::AsyncOutput {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = self;
                futures::join!(
                    $first.resolve(),
                    $($ty.resolve()),*
                )
            }

            fn keys(&self) -> Vec<NamedAttributeKey> {
                #[allow(non_snake_case)]
                let ($first, $($ty,)*) = &self;
                let mut buf = $first.keys();
                $(buf.extend($ty.keys());)*
                buf
            }
        }

        impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
        where
            $first: Attribute,
            $($ty: Attribute),*,

        {
            type Output<NewAttr: Attribute> = ($first, $($ty,)*);

            fn add_any_attr<NewAttr: Attribute>(
                self,
                _new_attr: NewAttr,
            ) -> Self::Output<NewAttr> {
                todo!("adding more than 26 attributes is not supported");
                //($first, $($ty,)*)
            }
        }
    };
}

impl<A> Attribute for (A,)
where
    A: Attribute,
{
    const MIN_LENGTH: usize = A::MIN_LENGTH;

    type AsyncOutput = (A::AsyncOutput,);
    type State = A::State;
    type Cloneable = (A::Cloneable,);
    type CloneableOwned = (A::CloneableOwned,);

    fn html_len(&self) -> usize {
        self.0.html_len()
    }

    fn to_html(
        self,
        buf: &mut String,
        class: &mut String,
        style: &mut String,
        inner_html: &mut String,
    ) {
        self.0.to_html(buf, class, style, inner_html);
    }

    fn hydrate<const FROM_SERVER: bool>(
        self,
        el: &crate::renderer::types::Element,
    ) -> Self::State {
        self.0.hydrate::<FROM_SERVER>(el)
    }

    fn build(self, el: &crate::renderer::types::Element) -> Self::State {
        self.0.build(el)
    }

    fn rebuild(self, state: &mut Self::State) {
        self.0.rebuild(state);
    }

    fn into_cloneable(self) -> Self::Cloneable {
        (self.0.into_cloneable(),)
    }

    fn into_cloneable_owned(self) -> Self::CloneableOwned {
        (self.0.into_cloneable_owned(),)
    }

    fn dry_resolve(&mut self) {
        self.0.dry_resolve();
    }

    async fn resolve(self) -> Self::AsyncOutput {
        (self.0.resolve().await,)
    }

    fn keys(&self) -> Vec<NamedAttributeKey> {
        self.0.keys()
    }
}

impl<A> NextAttribute for (A,)
where
    A: Attribute,
{
    next_attr_output_type!(A, NewAttr);

    fn add_any_attr<NewAttr: Attribute>(
        self,
        new_attr: NewAttr,
    ) -> Self::Output<NewAttr> {
        next_attr_combine!(self.0, new_attr)
    }
}

impl_attr_for_tuples!(A, B);
impl_attr_for_tuples!(A, B, C);
impl_attr_for_tuples!(A, B, C, D);
impl_attr_for_tuples!(A, B, C, D, E);
impl_attr_for_tuples!(A, B, C, D, E, F);
impl_attr_for_tuples!(A, B, C, D, E, F, G);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T
);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V
);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W
);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
);
impl_attr_for_tuples!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
impl_attr_for_tuples_truncate_additional!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
    Z
);
