use std::{any, borrow::Cow, fmt, ops, time::Duration};
use self::_private::{BoxedDeserializer, BoxedVisitor};
use crate::{
    de::{DeserializeParam, _private::ErasedDeserializer},
    fallback::FallbackSource,
    validation::Validate,
};
#[doc(hidden)] pub mod _private;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq))]
#[non_exhaustive]
pub struct AliasOptions {
    pub is_deprecated: bool,
}
impl Default for AliasOptions {
    fn default() -> Self {
        Self::new()
    }
}
impl AliasOptions {
    pub const fn new() -> Self {
        AliasOptions {
            is_deprecated: false,
        }
    }
    #[must_use]
    pub const fn deprecated(mut self) -> Self {
        self.is_deprecated = true;
        self
    }
    #[doc(hidden)] #[must_use]
    pub fn combine(self, other: Self) -> Self {
        Self {
            is_deprecated: self.is_deprecated || other.is_deprecated,
        }
    }
}
#[derive(Debug, Clone)]
pub struct ConfigMetadata {
    pub ty: RustType,
    pub help: &'static str,
    pub params: &'static [ParamMetadata],
    pub tag: Option<ConfigTag>,
    pub nested_configs: &'static [NestedConfigMetadata],
    #[doc(hidden)] pub deserializer: BoxedDeserializer,
    #[doc(hidden)] pub visitor: BoxedVisitor,
    #[doc(hidden)] pub validations: &'static [&'static dyn Validate<dyn any::Any>],
}
#[derive(Debug, Clone, Copy)]
pub struct ConfigTag {
    pub param: &'static ParamMetadata,
    pub variants: &'static [ConfigVariant],
    pub default_variant: Option<&'static ConfigVariant>,
}
#[derive(Debug, Clone, Copy)]
pub struct ConfigVariant {
    pub name: &'static str,
    pub aliases: &'static [&'static str],
    pub rust_name: &'static str,
    pub help: &'static str,
}
#[derive(Debug, Clone, Copy)]
pub struct ParamMetadata {
    pub name: &'static str,
    pub aliases: &'static [(&'static str, AliasOptions)],
    pub help: &'static str,
    pub rust_field_name: &'static str,
    pub rust_type: RustType,
    pub expecting: BasicTypes,
    pub tag_variant: Option<&'static ConfigVariant>,
    #[doc(hidden)] pub deserializer: &'static dyn ErasedDeserializer,
    #[doc(hidden)] pub default_value: Option<fn() -> Box<dyn any::Any>>,
    #[doc(hidden)] pub example_value: Option<fn() -> Box<dyn any::Any>>,
    #[doc(hidden)]
    pub fallback: Option<&'static dyn FallbackSource>,
}
impl ParamMetadata {
    pub fn default_value(&self) -> Option<Box<dyn any::Any>> {
        self.default_value.map(|value_fn| value_fn())
    }
    pub fn default_value_json(&self) -> Option<serde_json::Value> {
        self.default_value()
            .map(|val| self.deserializer.serialize_param(val.as_ref()))
    }
    pub fn example_value_json(&self) -> Option<serde_json::Value> {
        let example = self.example_value?();
        Some(self.deserializer.serialize_param(example.as_ref()))
    }
    pub fn type_description(&self) -> TypeDescription {
        let mut description = TypeDescription::default();
        self.deserializer.describe(&mut description);
        description.rust_type = self.rust_type.name_in_code;
        description
    }
}
#[derive(Clone, Copy)]
pub struct RustType {
    id: fn() -> any::TypeId,
    name_in_code: &'static str,
}
impl fmt::Debug for RustType {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.name_in_code)
    }
}
impl PartialEq for RustType {
    fn eq(&self, other: &Self) -> bool {
        (self.id)() == (other.id)()
    }
}
impl RustType {
    pub const fn of<T: 'static>(name_in_code: &'static str) -> Self {
        Self {
            id: any::TypeId::of::<T>,
            name_in_code,
        }
    }
    pub fn id(&self) -> any::TypeId {
        (self.id)()
    }
    pub const fn name_in_code(&self) -> &'static str {
        self.name_in_code
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct BasicTypes(u8);
impl BasicTypes {
    pub const BOOL: Self = Self(1);
    pub const INTEGER: Self = Self(2);
    pub const FLOAT: Self = Self(4 | 2);
    pub const STRING: Self = Self(8);
    pub const ARRAY: Self = Self(16);
    pub const OBJECT: Self = Self(32);
    pub const ANY: Self = Self(63);
    const COMPONENTS: &'static [(Self, &'static str)] = &[
        (Self::BOOL, "Boolean"),
        (Self::INTEGER, "integer"),
        (Self::FLOAT, "float"),
        (Self::STRING, "string"),
        (Self::ARRAY, "array"),
        (Self::OBJECT, "object"),
    ];
    pub(crate) const fn from_raw(raw: u8) -> Self {
        assert!(raw != 0, "Raw `BasicTypes` cannot be 0");
        assert!(
            raw <= Self::ANY.0,
            "Unused set bits in `BasicTypes` raw value"
        );
        Self(raw)
    }
    #[doc(hidden)] pub const fn raw(self) -> u8 {
        self.0
    }
    #[must_use]
    pub const fn or(self, rhs: Self) -> Self {
        Self(self.0 | rhs.0)
    }
    pub const fn contains(self, needle: Self) -> bool {
        self.0 & needle.0 == needle.0
    }
}
impl fmt::Display for BasicTypes {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        if *self == Self::ANY {
            formatter.write_str("any")
        } else {
            let mut is_empty = true;
            for &(component, name) in Self::COMPONENTS {
                if self.contains(component) {
                    if !is_empty {
                        formatter.write_str(" | ")?;
                    }
                    formatter.write_str(name)?;
                    is_empty = false;
                }
            }
            Ok(())
        }
    }
}
impl fmt::Debug for BasicTypes {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self, formatter)
    }
}
#[derive(Debug, Clone)]
struct ChildDescription {
    expecting: BasicTypes,
    description: Box<TypeDescription>,
}
impl ChildDescription {
    fn new<T: 'static, De: DeserializeParam<T>>(deserializer: &De, set_type: bool) -> Self {
        let mut description = Box::default();
        deserializer.describe(&mut description);
        if set_type {
            description.rust_type = any::type_name::<T>();
        }
        Self {
            expecting: De::EXPECTING,
            description,
        }
    }
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
#[doc(hidden)] pub enum TypeSuffixes {
    All,
    DurationUnits,
    SizeUnits,
}
#[derive(Debug, Clone, Default)]
pub struct TypeDescription {
    rust_type: &'static str,
    details: Option<Cow<'static, str>>,
    unit: Option<UnitOfMeasurement>,
    suffixes: Option<TypeSuffixes>,
    pub(crate) is_secret: bool,
    validations: Vec<String>,
    deserialize_if: Option<String>,
    items: Option<ChildDescription>,
    entries: Option<(ChildDescription, ChildDescription)>,
    fallback: Option<ChildDescription>,
}
impl TypeDescription {
    #[doc(hidden)]
    pub fn rust_type(&self) -> &str {
        self.rust_type
    }
    pub fn details(&self) -> Option<&str> {
        self.details.as_deref()
    }
    pub fn unit(&self) -> Option<UnitOfMeasurement> {
        self.unit
    }
    #[doc(hidden)] pub fn suffixes(&self) -> Option<TypeSuffixes> {
        self.suffixes
    }
    #[doc(hidden)] pub fn validations(&self) -> &[String] {
        &self.validations
    }
    #[doc(hidden)] pub fn deserialize_if(&self) -> Option<&str> {
        self.deserialize_if.as_deref()
    }
    pub fn items(&self) -> Option<(BasicTypes, &Self)> {
        self.items
            .as_ref()
            .map(|child| (child.expecting, &*child.description))
    }
    pub fn keys(&self) -> Option<(BasicTypes, &Self)> {
        let keys = &self.entries.as_ref()?.0;
        Some((keys.expecting, &*keys.description))
    }
    pub fn values(&self) -> Option<(BasicTypes, &Self)> {
        let keys = &self.entries.as_ref()?.1;
        Some((keys.expecting, &*keys.description))
    }
    pub fn fallback(&self) -> Option<(BasicTypes, &Self)> {
        let fallback = self.fallback.as_ref()?;
        Some((fallback.expecting, &*fallback.description))
    }
    pub fn contains_secrets(&self) -> bool {
        if self.is_secret {
            return true;
        }
        if let Some(item) = &self.items {
            if item.description.contains_secrets() {
                return true;
            }
        }
        if let Some((key, value)) = &self.entries {
            if key.description.contains_secrets() {
                return true;
            }
            if value.description.contains_secrets() {
                return true;
            }
        }
        false
    }
    pub fn set_details(&mut self, details: impl Into<Cow<'static, str>>) -> &mut Self {
        self.details = Some(details.into());
        self
    }
    pub fn set_unit(&mut self, unit: UnitOfMeasurement) -> &mut Self {
        self.unit = Some(unit);
        self
    }
    pub(crate) fn set_suffixes(&mut self, suffixes: TypeSuffixes) -> &mut Self {
        self.suffixes = Some(suffixes);
        self
    }
    pub fn set_validations<T>(&mut self, validations: &[&'static dyn Validate<T>]) -> &mut Self {
        self.validations = validations.iter().map(ToString::to_string).collect();
        self
    }
    pub fn set_deserialize_if<T>(&mut self, condition: &'static dyn Validate<T>) -> &mut Self {
        self.deserialize_if = Some(condition.to_string());
        self
    }
    pub fn set_secret(&mut self) -> &mut Self {
        self.is_secret = true;
        self
    }
    pub fn set_items<T: 'static>(&mut self, items: &impl DeserializeParam<T>) -> &mut Self {
        self.items = Some(ChildDescription::new(items, true));
        self
    }
    pub fn set_entries<K: 'static, V: 'static>(
        &mut self,
        keys: &impl DeserializeParam<K>,
        values: &impl DeserializeParam<V>,
    ) -> &mut Self {
        self.entries = Some((
            ChildDescription::new(keys, true),
            ChildDescription::new(values, true),
        ));
        self
    }
    pub fn set_fallback<T: 'static>(&mut self, fallback: &impl DeserializeParam<T>) {
        self.fallback = Some(ChildDescription::new(fallback, false));
    }
}
impl fmt::Display for TypeDescription {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(description) = &self.details {
            write!(formatter, ", {description}")?;
        }
        if let Some(unit) = self.unit {
            write!(formatter, " [unit: {unit}]")?;
        }
        Ok(())
    }
}
#[derive(Debug, Clone, Copy)]
pub struct NestedConfigMetadata {
    pub name: &'static str,
    pub aliases: &'static [(&'static str, AliasOptions)],
    pub rust_field_name: &'static str,
    pub tag_variant: Option<&'static ConfigVariant>,
    pub meta: &'static ConfigMetadata,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TimeUnit {
    Millis,
    Seconds,
    Minutes,
    Hours,
    Days,
    Weeks,
    }
impl TimeUnit {
    pub(crate) fn plural(self) -> &'static str {
        match self {
            TimeUnit::Millis => "milliseconds",
            TimeUnit::Seconds => "seconds",
            TimeUnit::Minutes => "minutes",
            TimeUnit::Hours => "hours",
            TimeUnit::Days => "days",
            TimeUnit::Weeks => "weeks",
        }
    }
    pub fn checked_mul(self, factor: u64) -> Option<Duration> {
        Some(match self {
            Self::Millis => Duration::from_millis(factor),
            Self::Seconds => Duration::from_secs(factor),
            Self::Minutes => {
                let val = factor.checked_mul(60)?;
                Duration::from_secs(val)
            }
            Self::Hours => {
                let val = factor.checked_mul(3_600)?;
                Duration::from_secs(val)
            }
            Self::Days => {
                let val = factor.checked_mul(86_400)?;
                Duration::from_secs(val)
            }
            Self::Weeks => {
                let val = factor.checked_mul(86_400 * 7)?;
                Duration::from_secs(val)
            }
        })
    }
}
impl fmt::Display for TimeUnit {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.plural())
    }
}
impl From<TimeUnit> for Duration {
    fn from(unit: TimeUnit) -> Self {
        match unit {
            TimeUnit::Millis => Duration::from_millis(1),
            TimeUnit::Seconds => Duration::from_secs(1),
            TimeUnit::Minutes => Duration::from_secs(60),
            TimeUnit::Hours => Duration::from_secs(3_600),
            TimeUnit::Days => Duration::from_secs(86_400),
            TimeUnit::Weeks => Duration::from_secs(86_400 * 7),
        }
    }
}
impl ops::Mul<u64> for TimeUnit {
    type Output = Duration;
    fn mul(self, rhs: u64) -> Self::Output {
        self.checked_mul(rhs)
            .unwrap_or_else(|| panic!("Integer overflow getting {rhs} * {self}"))
    }
}
impl ops::Mul<TimeUnit> for u64 {
    type Output = Duration;
    fn mul(self, rhs: TimeUnit) -> Self::Output {
        rhs.checked_mul(self)
            .unwrap_or_else(|| panic!("Integer overflow getting {self} * {rhs}"))
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SizeUnit {
    Bytes,
    KiB,
    MiB,
    GiB,
}
impl SizeUnit {
    pub(crate) const fn plural(self) -> &'static str {
        match self {
            Self::Bytes => "bytes",
            Self::KiB => "kilobytes",
            Self::MiB => "megabytes",
            Self::GiB => "gigabytes",
        }
    }
    pub(crate) const fn bytes_in_unit(self) -> u64 {
        match self {
            Self::Bytes => 1,
            Self::KiB => 1_024,
            Self::MiB => 1_024 * 1_024,
            Self::GiB => 1_024 * 1_024 * 1_024,
        }
    }
}
impl fmt::Display for SizeUnit {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.plural())
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum UnitOfMeasurement {
    Time(TimeUnit),
    ByteSize(SizeUnit),
}
impl fmt::Display for UnitOfMeasurement {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Time(unit) => fmt::Display::fmt(unit, formatter),
            Self::ByteSize(unit) => fmt::Display::fmt(unit, formatter),
        }
    }
}
impl From<TimeUnit> for UnitOfMeasurement {
    fn from(unit: TimeUnit) -> Self {
        Self::Time(unit)
    }
}
impl From<SizeUnit> for UnitOfMeasurement {
    fn from(unit: SizeUnit) -> Self {
        Self::ByteSize(unit)
    }
}