use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use ts_rs::TS;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct U64Range<const MIN: u64, const MAX: u64>(u64);

impl<const MIN: u64, const MAX: u64> TS for U64Range<MIN, MAX> {
    const EXPORT_TO: Option<&'static str> = Some("bindings/util/u64_range.ts");

    fn decl() -> String {
        <usize as TS>::decl()
    }

    fn name() -> String {
        <usize as TS>::name()
    }

    fn dependencies() -> Vec<ts_rs::Dependency> {
        vec![]
    }

    fn transparent() -> bool {
        true
    }
}

impl<const MIN: u64, const MAX: u64> Serialize for U64Range<MIN, MAX> {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_u64(self.0)
    }
}

impl<'de, const MIN: u64, const MAX: u64> serde::Deserialize<'de> for U64Range<MIN, MAX> {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let v = u64::deserialize(deserializer)?;
        if v < MIN || v > MAX {
            return Err(serde::de::Error::custom("out of range"));
        }

        Ok(U64Range(v))
    }
}

impl<const MIN: u64, const MAX: u64> std::ops::Deref for U64Range<MIN, MAX> {
    type Target = u64;

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

impl<const MIN: u64, const MAX: u64> From<U64Range<MIN, MAX>> for u64 {
    fn from(v: U64Range<MIN, MAX>) -> Self {
        v.0
    }
}

impl<const MIN: u64, const MAX: u64> std::fmt::Display for U64Range<MIN, MAX> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct OutOfRange;

impl<const MIN: u64, const MAX: u64> TryFrom<u64> for U64Range<MIN, MAX> {
    type Error = OutOfRange;

    fn try_from(value: u64) -> Result<Self, Self::Error> {
        if value < MIN || value > MAX {
            return Err(OutOfRange);
        }

        Ok(U64Range(value))
    }
}

pub(crate) fn deserialize_array_urlenc<'de, D, T: Eq + Hash>(
    deserializer: D,
) -> Result<Vec<T>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: DeserializeOwned,
{
    let str_raw = String::deserialize(deserializer)?;
    let parts = serde_urlencoded::from_str::<Vec<(T, String)>>(&str_raw)
        .map_err(serde::de::Error::custom)?
        .into_iter()
        .map(|(k, _)| k)
        .collect::<Vec<_>>();

    Ok(parts)
}

pub(crate) fn deserialize_opt_array_urlenc<'de, D, T: Eq + Hash>(
    deserializer: D,
) -> Result<Option<Vec<T>>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: DeserializeOwned,
{
    let Some(str_raw) = Option::<String>::deserialize(deserializer)? else {
        return Ok(None);
    };

    let parts = serde_urlencoded::from_str::<Vec<(T, String)>>(&str_raw)
        .map_err(serde::de::Error::custom)?
        .into_iter()
        .map(|(k, _)| k)
        .collect::<Vec<_>>();

    Ok(Some(parts))
}
