use std::{
    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
    fmt,
    hash::{BuildHasher, Hash},
    marker::PhantomData,
    sync::Arc,
};
use serde::de::{DeserializeOwned, Error as DeError};
use crate::{
    de::{DeserializeContext, DeserializeParam, WellKnown, WellKnownOption},
    error::{ErrorWithOrigin, LowLevelError},
    metadata::{BasicTypes, ParamMetadata, TypeDescription},
    utils::const_eq,
    value::{Map, StrValue, Value, ValueOrigin, WithOrigin},
};
#[derive(Debug)]
pub struct Repeated<De>(pub De);
impl<De> Repeated<De> {
    fn deserialize_array<T, C>(
        &self,
        mut ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
        expected_len: Option<usize>,
    ) -> Result<C, ErrorWithOrigin>
    where
        De: DeserializeParam<T>,
        C: FromIterator<T>,
    {
        let deserializer = ctx.current_value_deserializer(param.name)?;
        let Value::Array(items) = deserializer.value() else {
            return Err(deserializer.invalid_type("array"));
        };
        if let Some(expected_len) = expected_len {
            if items.len() != expected_len {
                let err = DeError::invalid_length(items.len(), &expected_len.to_string().as_str());
                return Err(deserializer.enrich_err(err));
            }
        }
        let mut has_errors = false;
        let items = items.iter().enumerate().filter_map(|(i, item)| {
            let coerced = item.coerce_value_type(De::EXPECTING);
            let mut child_ctx = ctx.child(&i.to_string(), ctx.location_in_config);
            let mut child_ctx = child_ctx.patched(coerced.as_ref().unwrap_or(item));
            match self.0.deserialize_param(child_ctx.borrow(), param) {
                Ok(val) if !has_errors => Some(val),
                Ok(_) => None, Err(err) => {
                    has_errors = true;
                    child_ctx.push_error(err);
                    None
                }
            }
        });
        let items: C = items.collect();
        if has_errors {
            let origin = deserializer.origin().clone();
            Err(ErrorWithOrigin::new(LowLevelError::InvalidArray, origin))
        } else {
            Ok(items)
        }
    }
}
macro_rules! impl_serialization_for_repeated {
    ($param:ty) => {
        fn deserialize_param(
            &self,
            ctx: DeserializeContext<'_>,
            param: &'static ParamMetadata,
        ) -> Result<$param, ErrorWithOrigin> {
            self.deserialize_array(ctx, param, None)
        }
        fn serialize_param(&self, param: &$param) -> serde_json::Value {
            let array = param
                .iter()
                .map(|item| self.0.serialize_param(item))
                .collect();
            serde_json::Value::Array(array)
        }
    };
}
impl<T: 'static, De> DeserializeParam<Vec<T>> for Repeated<De>
where
    De: DeserializeParam<T>,
{
    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
    fn describe(&self, description: &mut TypeDescription) {
        description.set_items(&self.0);
    }
    impl_serialization_for_repeated!(Vec<T>);
}
impl<T, S, De> DeserializeParam<HashSet<T, S>> for Repeated<De>
where
    T: 'static + Eq + Hash,
    S: 'static + Default + BuildHasher,
    De: DeserializeParam<T>,
{
    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
    fn describe(&self, description: &mut TypeDescription) {
        description.set_details("set").set_items(&self.0);
    }
    impl_serialization_for_repeated!(HashSet<T, S>);
}
impl<T, De> DeserializeParam<BTreeSet<T>> for Repeated<De>
where
    T: 'static + Eq + Ord,
    De: DeserializeParam<T>,
{
    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
    fn describe(&self, description: &mut TypeDescription) {
        description.set_details("set").set_items(&self.0);
    }
    impl_serialization_for_repeated!(BTreeSet<T>);
}
impl<T: 'static, De, const N: usize> DeserializeParam<[T; N]> for Repeated<De>
where
    De: DeserializeParam<T>,
{
    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
    fn describe(&self, description: &mut TypeDescription) {
        description
            .set_details(format!("{N}-element array"))
            .set_items(&self.0);
    }
    fn deserialize_param(
        &self,
        ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
    ) -> Result<[T; N], ErrorWithOrigin> {
        let items: Vec<_> = self.deserialize_array(ctx, param, Some(N))?;
        Ok(items.try_into().ok().unwrap())
    }
    fn serialize_param(&self, param: &[T; N]) -> serde_json::Value {
        let array = param
            .iter()
            .map(|item| self.0.serialize_param(item))
            .collect();
        serde_json::Value::Array(array)
    }
}
impl<T: WellKnown> WellKnown for Vec<T> {
    type Deserializer = Repeated<T::Deserializer>;
    const DE: Self::Deserializer = Repeated(T::DE);
}
impl<T: WellKnown> WellKnownOption for Vec<T> {}
impl<T: WellKnown, const N: usize> WellKnown for [T; N] {
    type Deserializer = Repeated<T::Deserializer>;
    const DE: Self::Deserializer = Repeated(T::DE);
}
impl<T: WellKnown, const N: usize> WellKnownOption for [T; N] {}
impl<T, S> WellKnown for HashSet<T, S>
where
    T: Eq + Hash + WellKnown,
    S: 'static + Default + BuildHasher,
{
    type Deserializer = Repeated<T::Deserializer>;
    const DE: Self::Deserializer = Repeated(T::DE);
}
impl<T, S> WellKnownOption for HashSet<T, S>
where
    T: Eq + Hash + WellKnown,
    S: 'static + Default + BuildHasher,
{
}
impl<T> WellKnown for BTreeSet<T>
where
    T: Eq + Ord + WellKnown,
{
    type Deserializer = Repeated<T::Deserializer>;
    const DE: Self::Deserializer = Repeated(T::DE);
}
impl<T> WellKnownOption for BTreeSet<T> where T: Eq + Ord + WellKnown {}
pub struct Entries<K, V, DeK = <K as WellKnown>::Deserializer, DeV = <V as WellKnown>::Deserializer>
{
    keys: DeK,
    values: DeV,
    _kv: PhantomData<fn(K, V)>,
}
impl<K, V, DeK: fmt::Debug, DeV: fmt::Debug> fmt::Debug for Entries<K, V, DeK, DeV> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("Entries")
            .field("keys", &self.keys)
            .field("values", &self.values)
            .finish()
    }
}
impl<K: WellKnown, V: WellKnown> Entries<K, V, K::Deserializer, V::Deserializer> {
    pub const WELL_KNOWN: Self = Self::new(K::DE, V::DE);
}
impl<K, V, DeK, DeV> Entries<K, V, DeK, DeV>
where
    DeK: DeserializeParam<K>,
    DeV: DeserializeParam<V>,
{
    pub const fn new(keys: DeK, values: DeV) -> Self {
        Self {
            keys,
            values,
            _kv: PhantomData,
        }
    }
    pub const fn named(
        self,
        keys_name: &'static str,
        values_name: &'static str,
    ) -> NamedEntries<K, V, DeK, DeV> {
        assert!(!keys_name.is_empty());
        assert!(!values_name.is_empty());
        assert!(
            !const_eq(keys_name.as_bytes(), values_name.as_bytes()),
            "Keys and values fields must not coincide"
        );
        NamedEntries {
            inner: self,
            keys_name,
            values_name,
        }
    }
    fn deserialize_map<C: FromIterator<(K, V)>>(
        &self,
        mut ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
        map: &Map,
        map_origin: &Arc<ValueOrigin>,
    ) -> Result<C, ErrorWithOrigin> {
        let mut has_errors = false;
        let items = map.iter().filter_map(|(key, value)| {
            let key_as_value = WithOrigin::new(
                key.clone().into(),
                Arc::new(ValueOrigin::Synthetic {
                    source: map_origin.clone(),
                    transform: "string key".into(),
                }),
            );
            let parsed_key =
                parse_key_or_value::<K, _>(&mut ctx, param, key, &self.keys, &key_as_value);
            let parsed_value =
                parse_key_or_value::<V, _>(&mut ctx, param, key, &self.values, value);
            has_errors |= parsed_key.is_none() || parsed_value.is_none();
            Some((parsed_key?, parsed_value?)).filter(|_| !has_errors)
        });
        let items: C = items.collect();
        if has_errors {
            let origin = map_origin.clone();
            Err(ErrorWithOrigin::new(LowLevelError::InvalidObject, origin))
        } else {
            Ok(items)
        }
    }
}
fn parse_key_or_value<T, De: DeserializeParam<T>>(
    ctx: &mut DeserializeContext<'_>,
    param: &'static ParamMetadata,
    key_path: &str,
    de: &De,
    val: &WithOrigin,
) -> Option<T> {
    let coerced = val.coerce_value_type(De::EXPECTING);
    let mut child_ctx = ctx.child(key_path, ctx.location_in_config);
    let mut child_ctx = child_ctx.patched(coerced.as_ref().unwrap_or(val));
    match de.deserialize_param(child_ctx.borrow(), param) {
        Ok(val) => Some(val),
        Err(err) => {
            child_ctx.push_error(err);
            None
        }
    }
}
pub trait ToEntry<'a, K, V>: Copy {
    fn to_entry(self) -> (&'a K, &'a V);
}
impl<'a, K, V> ToEntry<'a, K, V> for &'a (K, V) {
    fn to_entry(self) -> (&'a K, &'a V) {
        (&self.0, &self.1)
    }
}
impl<'a, K, V> ToEntry<'a, K, V> for (&'a K, &'a V) {
    fn to_entry(self) -> (&'a K, &'a V) {
        self
    }
}
pub trait ToEntries<K: 'static, V: 'static> {
    fn to_entries(&self) -> impl Iterator<Item = (&K, &V)>;
}
impl<K: 'static, V: 'static, C> ToEntries<K, V> for C
where
    for<'a> &'a C: IntoIterator<Item: ToEntry<'a, K, V>>,
{
    fn to_entries(&self) -> impl Iterator<Item = (&K, &V)> {
        self.into_iter().map(ToEntry::to_entry)
    }
}
impl<K, V, C, DeK, DeV> DeserializeParam<C> for Entries<K, V, DeK, DeV>
where
    K: 'static,
    V: 'static,
    DeK: DeserializeParam<K>,
    DeV: DeserializeParam<V>,
    C: FromIterator<(K, V)> + ToEntries<K, V>,
{
    const EXPECTING: BasicTypes = {
        assert!(
            DeK::EXPECTING.contains(BasicTypes::STRING)
                || DeK::EXPECTING.contains(BasicTypes::INTEGER),
            "map keys must be deserializable from strings or ints"
        );
        BasicTypes::OBJECT
    };
    fn describe(&self, description: &mut TypeDescription) {
        description
            .set_details("map")
            .set_entries(&self.keys, &self.values);
    }
    fn deserialize_param(
        &self,
        ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
    ) -> Result<C, ErrorWithOrigin> {
        let deserializer = ctx.current_value_deserializer(param.name)?;
        let Value::Object(map) = deserializer.value() else {
            return Err(deserializer.invalid_type("object"));
        };
        self.deserialize_map(ctx, param, map, deserializer.origin())
    }
    fn serialize_param(&self, param: &C) -> serde_json::Value {
        let object = param
            .to_entries()
            .map(|(key, value)| {
                let key = match self.keys.serialize_param(key) {
                    serde_json::Value::String(s) => s,
                    serde_json::Value::Number(num) => num.to_string(),
                    _ => panic!("unsupported key value"),
                };
                let value = self.values.serialize_param(value);
                (key, value)
            })
            .collect();
        serde_json::Value::Object(object)
    }
}
impl<K, V, S> WellKnown for HashMap<K, V, S>
where
    K: 'static + Eq + Hash + WellKnown,
    V: 'static + WellKnown,
    S: 'static + Default + BuildHasher,
{
    type Deserializer = Entries<K, V, K::Deserializer, V::Deserializer>;
    const DE: Self::Deserializer = Entries::new(K::DE, V::DE);
}
impl<K, V, S> WellKnownOption for HashMap<K, V, S>
where
    K: 'static + Eq + Hash + WellKnown,
    V: 'static + WellKnown,
    S: 'static + Default + BuildHasher,
{
}
impl<K, V> WellKnown for BTreeMap<K, V>
where
    K: 'static + Eq + Ord + WellKnown,
    V: 'static + WellKnown,
{
    type Deserializer = Entries<K, V, K::Deserializer, V::Deserializer>;
    const DE: Self::Deserializer = Entries::new(K::DE, V::DE);
}
impl<K, V> WellKnownOption for BTreeMap<K, V>
where
    K: 'static + Eq + Ord + WellKnown,
    V: 'static + WellKnown,
{
}
#[derive(Debug)]
pub struct Delimited(pub &'static str);
impl<T: DeserializeOwned + WellKnown> DeserializeParam<T> for Delimited {
    const EXPECTING: BasicTypes = {
        let base = <T::Deserializer as DeserializeParam<T>>::EXPECTING;
        assert!(
            base.contains(BasicTypes::ARRAY),
            "can only apply `Delimited` to types that support deserialization from array"
        );
        base.or(BasicTypes::STRING)
    };
    fn describe(&self, description: &mut TypeDescription) {
        T::DE.describe(description);
        let details = if let Some(details) = description.details() {
            format!("{details}; using {:?} delimiter", self.0)
        } else {
            format!("using {:?} delimiter", self.0)
        };
        description.set_details(details);
    }
    fn deserialize_param(
        &self,
        mut ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
    ) -> Result<T, ErrorWithOrigin> {
        let Some(WithOrigin {
            inner: Value::String(s),
            origin,
        }) = ctx.current_value()
        else {
            return T::DE.deserialize_param(ctx, param);
        };
        let array_origin = Arc::new(ValueOrigin::Synthetic {
            source: origin.clone(),
            transform: format!("{:?}-delimited string", self.0),
        });
        let array_items = s.expose().split(self.0).enumerate().map(|(i, part)| {
            let item_origin = ValueOrigin::Path {
                source: array_origin.clone(),
                path: i.to_string(),
            };
            let part = if s.is_secret() {
                StrValue::Secret(part.into())
            } else {
                StrValue::Plain(part.into())
            };
            WithOrigin::new(Value::String(part), Arc::new(item_origin))
        });
        let array = WithOrigin::new(Value::Array(array_items.collect()), array_origin);
        T::DE.deserialize_param(ctx.patched(&array), param)
    }
    fn serialize_param(&self, param: &T) -> serde_json::Value {
        T::DE.serialize_param(param)
    }
}
pub struct NamedEntries<
    K,
    V,
    DeK = <K as WellKnown>::Deserializer,
    DeV = <V as WellKnown>::Deserializer,
> {
    inner: Entries<K, V, DeK, DeV>,
    keys_name: &'static str,
    values_name: &'static str,
}
impl<K, V, DeK: fmt::Debug, DeV: fmt::Debug> fmt::Debug for NamedEntries<K, V, DeK, DeV> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("NamedEntries")
            .field("inner", &self.inner)
            .field("keys_name", &self.keys_name)
            .field("values_name", &self.values_name)
            .finish()
    }
}
impl<K, V, DeK, DeV> NamedEntries<K, V, DeK, DeV>
where
    DeK: DeserializeParam<K>,
    DeV: DeserializeParam<V>,
{
    fn deserialize_entries<C: FromIterator<(K, V)>>(
        &self,
        mut ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
        array: &[WithOrigin],
        array_origin: &Arc<ValueOrigin>,
    ) -> Result<C, ErrorWithOrigin> {
        let mut has_errors = false;
        let items = array.iter().enumerate().filter_map(|(i, entry)| {
            let idx_str = i.to_string();
            let (key, value) = match self.parse_entry(entry) {
                Ok(entry) => entry,
                Err(err) => {
                    ctx.child(&idx_str, ctx.location_in_config).push_error(err);
                    has_errors = true;
                    return None;
                }
            };
            let parsed_key =
                parse_key_or_value::<K, _>(&mut ctx, param, &idx_str, &self.inner.keys, key);
            let null_value;
            let value = if let Some(value) = value {
                value
            } else {
                let null_origin = ValueOrigin::Synthetic {
                    source: entry.origin.clone(),
                    transform: "missing entry value".to_owned(),
                };
                null_value = WithOrigin::new(Value::Null, Arc::new(null_origin));
                &null_value
            };
            let parsed_value =
                parse_key_or_value::<V, _>(&mut ctx, param, &idx_str, &self.inner.values, value);
            has_errors |= parsed_key.is_none() || parsed_value.is_none();
            Some((parsed_key?, parsed_value?)).filter(|_| !has_errors)
        });
        let items: C = items.collect();
        if has_errors {
            let origin = array_origin.clone();
            Err(ErrorWithOrigin::new(LowLevelError::InvalidArray, origin))
        } else {
            Ok(items)
        }
    }
    fn parse_entry<'a>(
        &self,
        entry: &'a WithOrigin,
    ) -> Result<(&'a WithOrigin, Option<&'a WithOrigin>), ErrorWithOrigin> {
        let Value::Object(obj) = &entry.inner else {
            let expected = format!(
                "{{ {:?}: _, {:?}: _ }} tuple",
                self.keys_name, self.values_name
            );
            return Err(entry.invalid_type(&expected));
        };
        let key = obj.get(self.keys_name).ok_or_else(|| {
            let err = DeError::missing_field(self.keys_name);
            ErrorWithOrigin::json(err, entry.origin.clone())
        })?;
        let value = obj.get(self.values_name);
        if obj.len() > 2 {
            let expected = format!(
                "{{ {:?}: _, {:?}: _ }} tuple",
                self.keys_name, self.values_name
            );
            return Err(entry.invalid_type(&expected));
        }
        Ok((key, value))
    }
}
impl<K, V, DeK, DeV, C> DeserializeParam<C> for NamedEntries<K, V, DeK, DeV>
where
    K: 'static,
    V: 'static,
    DeK: DeserializeParam<K>,
    DeV: DeserializeParam<V>,
    C: FromIterator<(K, V)> + ToEntries<K, V>,
{
    const EXPECTING: BasicTypes = BasicTypes::OBJECT.or(BasicTypes::ARRAY);
    fn describe(&self, description: &mut TypeDescription) {
        let details = format!(
            "map or array of {{ {:?}: _, {:?}: _ }} tuples",
            self.keys_name, self.values_name
        );
        description
            .set_details(details)
            .set_entries(&self.inner.keys, &self.inner.values);
    }
    fn deserialize_param(
        &self,
        ctx: DeserializeContext<'_>,
        param: &'static ParamMetadata,
    ) -> Result<C, ErrorWithOrigin> {
        let deserializer = ctx.current_value_deserializer(param.name)?;
        match deserializer.value() {
            Value::Object(map) => {
                self.inner
                    .deserialize_map(ctx, param, map, deserializer.origin())
            }
            Value::Array(array) => {
                self.deserialize_entries(ctx, param, array, deserializer.origin())
            }
            _ => Err(deserializer.invalid_type("object or array")),
        }
    }
    fn serialize_param(&self, param: &C) -> serde_json::Value {
        let entries = param.to_entries().map(|(key, value)| {
            let key = self.inner.keys.serialize_param(key);
            let value = self.inner.values.serialize_param(value);
            serde_json::json!({
                self.keys_name: key,
                self.values_name: value,
            })
        });
        serde_json::Value::Array(entries.collect())
    }
}