use crate::core::{SignalMarker, SignalStrMarker};
use leptos::prelude::*;
use leptos::reactive::wrappers::read::Signal;
use std::ops::Deref;
#[cfg_attr(not(debug_assertions), repr(transparent))]
pub struct ElementsMaybeSignal<T: 'static> {
    #[cfg(debug_assertions)]
    defined_at: &'static std::panic::Location<'static>,
    inner: ElementsMaybeSignalType<T>,
}
impl<T> Clone for ElementsMaybeSignal<T> {
    fn clone(&self) -> Self {
        *self
    }
}
impl<T> Copy for ElementsMaybeSignal<T> {}
pub enum ElementsMaybeSignalType<T: 'static> {
    Static(StoredValue<Vec<Option<T>>, LocalStorage>),
    Dynamic(Signal<Vec<Option<T>>, LocalStorage>),
}
impl<T> Clone for ElementsMaybeSignalType<T> {
    fn clone(&self) -> Self {
        *self
    }
}
impl<T> Copy for ElementsMaybeSignalType<T> {}
impl<T: 'static> Default for ElementsMaybeSignalType<T> {
    fn default() -> Self {
        Self::Static(StoredValue::new_local(vec![]))
    }
}
impl<T> Default for ElementsMaybeSignal<T> {
    fn default() -> Self {
        Self {
            inner: ElementsMaybeSignalType::default(),
            #[cfg(debug_assertions)]
            defined_at: std::panic::Location::caller(),
        }
    }
}
impl<T> DefinedAt for ElementsMaybeSignal<T> {
    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
        #[cfg(debug_assertions)]
        {
            Some(self.defined_at)
        }
        #[cfg(not(debug_assertions))]
        {
            None
        }
    }
}
impl<T> With for ElementsMaybeSignal<T> {
    type Value = Vec<Option<T>>;
    fn try_with<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
        match &self.inner {
            ElementsMaybeSignalType::Static(v) => v.try_with_value(f),
            ElementsMaybeSignalType::Dynamic(s) => s.try_with(f),
        }
    }
}
impl<T> WithUntracked for ElementsMaybeSignal<T> {
    type Value = Vec<Option<T>>;
    fn try_with_untracked<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
        match self.inner {
            ElementsMaybeSignalType::Static(t) => t.try_with_value(f),
            ElementsMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
        }
    }
}
pub trait IntoElementsMaybeSignal<T, Marker: ?Sized> {
    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T>;
}
impl<El, T, Marker: ?Sized> IntoElementsMaybeSignal<T, Marker> for El
where
    El: IntoElementsMaybeSignalType<T, Marker>,
{
    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T> {
        ElementsMaybeSignal {
            inner: self.into_elements_maybe_signal_type(),
            #[cfg(debug_assertions)]
            defined_at: std::panic::Location::caller(),
        }
    }
}
pub trait IntoElementsMaybeSignalType<T, Marker: ?Sized> {
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T>;
}
impl<T, Js> IntoElementsMaybeSignalType<T, web_sys::Element> for Js
where
    T: From<Js> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![Some(T::from(self).clone())]))
    }
}
impl<T, Js> IntoElementsMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
where
    T: From<Js> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![
            self.map(|el| T::from(el).clone())
        ]))
    }
}
impl<T, E, Js> IntoElementsMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
where
    Js: Deref<Target = Option<E>>,
    E: Clone,
    T: From<E> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![self
            .as_ref()
            .map(|e| T::from(e.clone()))]))
    }
}
impl<T, V> IntoElementsMaybeSignalType<T, str> for V
where
    V: AsRef<str>,
    T: From<web_sys::Element> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        if cfg!(feature = "ssr") {
            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
        } else {
            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![document()
                .query_selector(self.as_ref())
                .unwrap_or_default()
                .map(|el| T::from(el).clone())]))
        }
    }
}
impl<T, V, I> IntoElementsMaybeSignalType<T, SignalStrMarker> for V
where
    V: Get<Value = I> + 'static,
    I: AsRef<str>,
    T: From<web_sys::Element> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        if cfg!(feature = "ssr") {
            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
        } else {
            ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
                vec![document()
                    .query_selector(self.get().as_ref())
                    .unwrap_or_default()
                    .map(|el| T::from(el).clone())]
            }))
        }
    }
}
impl<T, V, E> IntoElementsMaybeSignalType<T, SignalMarker> for V
where
    V: Get<Value = E> + 'static,
    T: From<E> + Clone,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
            vec![Some(T::from(self.get()))]
        }))
    }
}
impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [web_sys::Element]> for C
where
    Js: Clone + 'a,
    T: From<Js>,
    C: IntoIterator<Item = &'a Js>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(
            self.into_iter().map(|t| Some(T::from(t.clone()))).collect(),
        ))
    }
}
impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [Option<web_sys::Element>]> for C
where
    Js: Clone + 'a,
    T: From<Js>,
    C: IntoIterator<Item = &'a Option<Js>>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(
            self.into_iter().map(|t| t.clone().map(T::from)).collect(),
        ))
    }
}
impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<web_sys::Element>> for C
where
    T: From<Js> + Clone,
    C: IntoIterator<Item = Js>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(
            self.into_iter().map(|t| Some(T::from(t))).collect(),
        ))
    }
}
impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<Option<web_sys::Element>>> for C
where
    T: From<Js> + Clone,
    C: IntoIterator<Item = Option<Js>>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Static(StoredValue::new_local(
            self.into_iter().map(|t| t.map(T::from)).collect(),
        ))
    }
}
impl<T, V, C> IntoElementsMaybeSignalType<T, &[&str]> for C
where
    V: AsRef<str>,
    T: From<web_sys::Element> + Clone,
    C: IntoIterator<Item = V>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        if cfg!(feature = "ssr") {
            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
        } else {
            ElementsMaybeSignalType::Static(StoredValue::new_local(
                self.into_iter()
                    .map(|sel| {
                        document()
                            .query_selector(sel.as_ref())
                            .unwrap_or_default()
                            .map(|el| T::from(el).clone())
                    })
                    .collect(),
            ))
        }
    }
}
pub struct SignalVecMarker;
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecMarker> for G
where
    T: From<Js> + Clone,
    G: Get<Value = C> + 'static,
    C: IntoIterator<Item = Js>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
            self.get().into_iter().map(|t| Some(T::from(t))).collect()
        }))
    }
}
pub struct SignalVecOptionMarker;
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecOptionMarker> for G
where
    T: From<Js> + Clone,
    G: Get<Value = C> + 'static,
    C: IntoIterator<Item = Option<Js>>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
            self.get().into_iter().map(|t| t.map(T::from)).collect()
        }))
    }
}
pub struct VecSignalMarker;
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalMarker> for C
where
    T: From<Js> + Clone,
    C: IntoIterator<Item = G> + Clone + 'static,
    G: Get<Value = Js>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        let signals = self.clone();
        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
            signals
                .clone()
                .into_iter()
                .map(|t| Some(T::from(t.get())))
                .collect()
        }))
    }
}
pub struct VecSignalOptionMarker;
impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalOptionMarker> for C
where
    T: From<Js> + Clone,
    C: IntoIterator<Item = G> + Clone + 'static,
    G: Get<Value = Option<Js>>,
{
    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
        let signals = self.clone();
        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
            signals
                .clone()
                .into_iter()
                .map(|t| t.get().map(T::from))
                .collect()
        }))
    }
}