//! Traits desccribing basic activitystreams types

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

use url::Url;

static PUBLIC: &str = "https://www.w3.org/ns/activitystreams#Public";

/// A type that represents a Public Key
pub trait PublicKey {
    /// The Public Key's ID
    fn id(&self) -> &Url;

    /// The Public Key's Owner
    fn owner(&self) -> &Url;

    /// The Public Key's pem-encoded value
    fn public_key_pem(&self) -> &str;
}

/// A type that represents an Object
pub trait Object {
    /// The Kind of object, e.g. Note, Video, Image, etc.
    type Kind;

    /// The Object's ID
    fn id(&self) -> &Url;

    /// The kind of Object
    fn kind(&self) -> &Self::Kind;
}

/// An object that has associated delivery fields
pub trait DeliverableObject: Object {
    /// Who the object is intended for
    fn to(&self) -> &[Url];

    /// Additional actors the object should be delivered to
    fn cc(&self) -> &[Url];

    /// Determine whether an object is public
    fn is_public(&self) -> bool {
        self.to()
            .iter()
            .chain(self.cc())
            .any(|url| url.as_str() == PUBLIC)
    }
}

/// A type that represents an Activity
pub trait Activity: DeliverableObject {
    /// The Actor performing the activity
    fn actor_id(&self) -> &Url;

    /// The object being performed upon
    fn object_id(&self) -> &Url;
}

/// A type that represents an Actor
pub trait Actor: Object {
    /// The actor's inbox
    fn inbox(&self) -> &Url;

    /// The actor's outbox
    fn outbox(&self) -> &Url;

    /// The actor's preferred username (handle)
    fn preferred_username(&self) -> &str;

    /// The actor's public key ID
    fn public_key_id(&self) -> &Url;

    /// The actor's display name
    fn name(&self) -> Option<&str>;

    /// The actor's shared inbox
    fn shared_inbox(&self) -> Option<&Url>;

    /// Determine the name of an actor
    fn canonical_name(&self) -> &str {
        self.name().unwrap_or_else(|| self.preferred_username())
    }

    /// Determine which inbox an object should be delievered to
    fn delivery_inbox<O: DeliverableObject>(&self, obj: &O) -> &Url {
        if obj.is_public() {
            self.shared_inbox().unwrap_or_else(|| self.inbox())
        } else {
            self.inbox()
        }
    }
}

impl<'a, T> PublicKey for &'a T
where
    T: PublicKey,
{
    fn id(&self) -> &Url {
        T::id(self)
    }

    fn owner(&self) -> &Url {
        T::owner(self)
    }

    fn public_key_pem(&self) -> &str {
        T::public_key_pem(self)
    }
}

impl<'a, T> PublicKey for &'a mut T
where
    T: PublicKey,
{
    fn id(&self) -> &Url {
        T::id(self)
    }

    fn owner(&self) -> &Url {
        T::owner(self)
    }

    fn public_key_pem(&self) -> &str {
        T::public_key_pem(self)
    }
}

impl<T> PublicKey for Box<T>
where
    T: PublicKey,
{
    fn id(&self) -> &Url {
        T::id(self)
    }

    fn owner(&self) -> &Url {
        T::owner(self)
    }

    fn public_key_pem(&self) -> &str {
        T::public_key_pem(self)
    }
}

impl<T> PublicKey for Rc<T>
where
    T: PublicKey,
{
    fn id(&self) -> &Url {
        T::id(self)
    }

    fn owner(&self) -> &Url {
        T::owner(self)
    }

    fn public_key_pem(&self) -> &str {
        T::public_key_pem(self)
    }
}

impl<T> PublicKey for Arc<T>
where
    T: PublicKey,
{
    fn id(&self) -> &Url {
        T::id(self)
    }

    fn owner(&self) -> &Url {
        T::owner(self)
    }

    fn public_key_pem(&self) -> &str {
        T::public_key_pem(self)
    }
}

impl<'a, T> Object for &'a T
where
    T: Object,
{
    type Kind = T::Kind;

    fn id(&self) -> &Url {
        T::id(self)
    }

    fn kind(&self) -> &Self::Kind {
        T::kind(self)
    }
}

impl<'a, T> Object for &'a mut T
where
    T: Object,
{
    type Kind = T::Kind;

    fn id(&self) -> &Url {
        T::id(self)
    }

    fn kind(&self) -> &Self::Kind {
        T::kind(self)
    }
}

impl<T> Object for Box<T>
where
    T: Object,
{
    type Kind = T::Kind;

    fn id(&self) -> &Url {
        T::id(self)
    }

    fn kind(&self) -> &Self::Kind {
        T::kind(self)
    }
}

impl<T> Object for Rc<T>
where
    T: Object,
{
    type Kind = T::Kind;

    fn id(&self) -> &Url {
        T::id(self)
    }

    fn kind(&self) -> &Self::Kind {
        T::kind(self)
    }
}

impl<T> Object for Arc<T>
where
    T: Object,
{
    type Kind = T::Kind;

    fn id(&self) -> &Url {
        T::id(self)
    }

    fn kind(&self) -> &Self::Kind {
        T::kind(self)
    }
}

impl<'a, T> DeliverableObject for &'a T
where
    T: DeliverableObject,
{
    fn to(&self) -> &[Url] {
        T::to(self)
    }

    fn cc(&self) -> &[Url] {
        T::cc(self)
    }
}

impl<'a, T> DeliverableObject for &'a mut T
where
    T: DeliverableObject,
{
    fn to(&self) -> &[Url] {
        T::to(self)
    }

    fn cc(&self) -> &[Url] {
        T::cc(self)
    }
}

impl<T> DeliverableObject for Box<T>
where
    T: DeliverableObject,
{
    fn to(&self) -> &[Url] {
        T::to(self)
    }

    fn cc(&self) -> &[Url] {
        T::cc(self)
    }
}

impl<T> DeliverableObject for Rc<T>
where
    T: DeliverableObject,
{
    fn to(&self) -> &[Url] {
        T::to(self)
    }

    fn cc(&self) -> &[Url] {
        T::cc(self)
    }
}

impl<T> DeliverableObject for Arc<T>
where
    T: DeliverableObject,
{
    fn to(&self) -> &[Url] {
        T::to(self)
    }

    fn cc(&self) -> &[Url] {
        T::cc(self)
    }
}

impl<'a, T> Activity for &'a T
where
    T: Activity,
{
    fn actor_id(&self) -> &Url {
        T::actor_id(self)
    }

    fn object_id(&self) -> &Url {
        T::object_id(self)
    }
}

impl<'a, T> Activity for &'a mut T
where
    T: Activity,
{
    fn actor_id(&self) -> &Url {
        T::actor_id(self)
    }

    fn object_id(&self) -> &Url {
        T::object_id(self)
    }
}

impl<T> Activity for Box<T>
where
    T: Activity,
{
    fn actor_id(&self) -> &Url {
        T::actor_id(self)
    }

    fn object_id(&self) -> &Url {
        T::object_id(self)
    }
}

impl<T> Activity for Rc<T>
where
    T: Activity,
{
    fn actor_id(&self) -> &Url {
        T::actor_id(self)
    }

    fn object_id(&self) -> &Url {
        T::object_id(self)
    }
}

impl<T> Activity for Arc<T>
where
    T: Activity,
{
    fn actor_id(&self) -> &Url {
        T::actor_id(self)
    }

    fn object_id(&self) -> &Url {
        T::object_id(self)
    }
}

impl<'a, T> Actor for &'a T
where
    T: Actor,
{
    fn inbox(&self) -> &Url {
        T::inbox(self)
    }

    fn outbox(&self) -> &Url {
        T::outbox(self)
    }

    fn preferred_username(&self) -> &str {
        T::preferred_username(self)
    }

    fn public_key_id(&self) -> &Url {
        T::public_key_id(self)
    }

    fn name(&self) -> Option<&str> {
        T::name(self)
    }

    fn shared_inbox(&self) -> Option<&Url> {
        T::shared_inbox(self)
    }
}

impl<'a, T> Actor for &'a mut T
where
    T: Actor,
{
    fn inbox(&self) -> &Url {
        T::inbox(self)
    }

    fn outbox(&self) -> &Url {
        T::outbox(self)
    }

    fn preferred_username(&self) -> &str {
        T::preferred_username(self)
    }

    fn public_key_id(&self) -> &Url {
        T::public_key_id(self)
    }

    fn name(&self) -> Option<&str> {
        T::name(self)
    }

    fn shared_inbox(&self) -> Option<&Url> {
        T::shared_inbox(self)
    }
}

impl<T> Actor for Box<T>
where
    T: Actor,
{
    fn inbox(&self) -> &Url {
        T::inbox(self)
    }

    fn outbox(&self) -> &Url {
        T::outbox(self)
    }

    fn preferred_username(&self) -> &str {
        T::preferred_username(self)
    }

    fn public_key_id(&self) -> &Url {
        T::public_key_id(self)
    }

    fn name(&self) -> Option<&str> {
        T::name(self)
    }

    fn shared_inbox(&self) -> Option<&Url> {
        T::shared_inbox(self)
    }
}

impl<T> Actor for Rc<T>
where
    T: Actor,
{
    fn inbox(&self) -> &Url {
        T::inbox(self)
    }

    fn outbox(&self) -> &Url {
        T::outbox(self)
    }

    fn preferred_username(&self) -> &str {
        T::preferred_username(self)
    }

    fn public_key_id(&self) -> &Url {
        T::public_key_id(self)
    }

    fn name(&self) -> Option<&str> {
        T::name(self)
    }

    fn shared_inbox(&self) -> Option<&Url> {
        T::shared_inbox(self)
    }
}

impl<T> Actor for Arc<T>
where
    T: Actor,
{
    fn inbox(&self) -> &Url {
        T::inbox(self)
    }

    fn outbox(&self) -> &Url {
        T::outbox(self)
    }

    fn preferred_username(&self) -> &str {
        T::preferred_username(self)
    }

    fn public_key_id(&self) -> &Url {
        T::public_key_id(self)
    }

    fn name(&self) -> Option<&str> {
        T::name(self)
    }

    fn shared_inbox(&self) -> Option<&Url> {
        T::shared_inbox(self)
    }
}
