//! Traits describing the delivery of objects to remote servers

use std::{rc::Rc, sync::Arc};
use url::Url;

use crate::session::Session;

/// Describes sending an activity to a server
///
/// When implementing this trait, things like HTTP Signatures and HTTP Digests should be considered
///
/// ```rust
/// use apub_core::{deliver::Deliver, session::Session};
/// use url::Url;
///
/// pub struct MyClient {
///     inner: reqwest::Client,
/// }
///
/// #[async_trait::async_trait(?Send)]
/// impl Deliver for MyClient {
///     type Error = reqwest::Error;
///
///     async fn deliver<T: serde::ser::Serialize, S: Session>(&self, inbox: &Url, activity: &T, _: S) -> Result<(), Self::Error> {
///         self.inner.post(inbox.as_str()).json(activity).send().await?;
///         Ok(())
///     }
/// }
/// ```
#[async_trait::async_trait(?Send)]
pub trait Deliver {
    /// Errors produced by the client
    type Error: std::fmt::Display + 'static;

    /// Deliver the activity to the inbox
    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error>;
}

/// The inverse of [`Deliver`], allows for `item.deliver(&client)` syntax
///
/// ```rust
/// use apub_core::deliver::Deliverable;
/// use url::Url;
///
/// #[derive(serde::Deserialize, serde::Serialize)]
/// pub struct MyActivity {
///     id: Url,
///     // etc...
/// }
///
/// impl Deliverable for MyActivity {}
/// ```
#[async_trait::async_trait(?Send)]
pub trait Deliverable: serde::ser::Serialize {
    /// Deliver the activity to the inbox
    async fn deliver<D: Deliver, S: Session>(
        &self,
        inbox: &Url,
        client: &D,
        session: S,
    ) -> Result<(), D::Error>
    where
        Self: Sized,
    {
        client.deliver(inbox, self, session).await
    }
}

#[async_trait::async_trait(?Send)]
impl<'a, D> Deliver for &'a D
where
    D: Deliver,
{
    type Error = D::Error;

    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error> {
        D::deliver(self, inbox, activity, session).await
    }
}

#[async_trait::async_trait(?Send)]
impl<'a, D> Deliver for &'a mut D
where
    D: Deliver,
{
    type Error = D::Error;

    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error> {
        D::deliver(self, inbox, activity, session).await
    }
}

#[async_trait::async_trait(?Send)]
impl<D> Deliver for Box<D>
where
    D: Deliver,
{
    type Error = D::Error;

    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error> {
        D::deliver(self, inbox, activity, session).await
    }
}

#[async_trait::async_trait(?Send)]
impl<D> Deliver for Rc<D>
where
    D: Deliver,
{
    type Error = D::Error;

    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error> {
        D::deliver(self, inbox, activity, session).await
    }
}

#[async_trait::async_trait(?Send)]
impl<D> Deliver for Arc<D>
where
    D: Deliver,
{
    type Error = D::Error;

    async fn deliver<T: serde::ser::Serialize, S: Session>(
        &self,
        inbox: &Url,
        activity: &T,
        session: S,
    ) -> Result<(), Self::Error> {
        D::deliver(self, inbox, activity, session).await
    }
}
