//! A provided ObjectId type

use std::{
    borrow::Cow,
    fmt::Display,
    marker::PhantomData,
    ops::{Deref, DerefMut},
};
use url::Url;

/// A type generic over a `Kind` but contains only an internal Url
///
/// This type likely shouldn't be used directly, but is a helper for building your own ObjectId
/// type.
///
/// ```rust
/// use apub_core::repo::Dereference;
/// use url::Url;
///
/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
/// #[serde(transparent)]
/// pub struct ObjectId<Kind>(apub_core::object_id::ObjectId<'static, Kind>);
///
/// pub fn object_id<Kind>(url: Url) -> ObjectId<Kind> {
///     ObjectId(apub_core::object_id::ObjectId::new_owned(url))
/// }
///
/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
/// struct MyKind;
/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
/// struct MyObject {
///     id: ObjectId<MyKind>,
/// }
///
/// impl Dereference for ObjectId<MyKind> {
///     type Output = MyObject;
///
///     fn url(&self) -> &Url {
///         &self.0
///     }
/// }
/// ```
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ObjectId<'a, Kind> {
    url: Cow<'a, Url>,
    _kind: PhantomData<fn() -> Kind>,
}

impl<'a, Kind> Clone for ObjectId<'a, Kind> {
    fn clone(&self) -> Self {
        Self {
            url: self.url.clone(),
            _kind: PhantomData,
        }
    }
}

impl<Kind> ObjectId<'static, Kind> {
    /// Create a new owned ObjectId
    pub fn new_owned(url: Url) -> Self {
        Self {
            url: Cow::Owned(url),
            _kind: PhantomData,
        }
    }
}

impl<'a, Kind> ObjectId<'a, Kind> {
    /// Create a new borrowed ObjectId
    ///
    /// This is useful for creating a valid Dereference type from a borrowed Url without cloning
    pub fn new_borrowed(url: &'a Url) -> Self {
        Self {
            url: Cow::Borrowed(url),
            _kind: PhantomData,
        }
    }
}

impl<'a, Kind> Deref for ObjectId<'a, Kind> {
    type Target = Url;

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

impl<'a, Kind> DerefMut for ObjectId<'a, Kind> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.url.to_mut()
    }
}

impl<'a, Kind> Display for ObjectId<'a, Kind> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.url.fmt(f)
    }
}

impl<'a, Kind> serde::ser::Serialize for ObjectId<'a, Kind> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.url.serialize(serializer)
    }
}

impl<'de, Kind> serde::de::Deserialize<'de> for ObjectId<'static, Kind> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Url::deserialize(deserializer).map(ObjectId::new_owned)
    }
}
