use activitystreams::actor::{ApActor, Person};
use activitystreams::iri_string::types::IriString;
use activitystreams::object::{Article, Image, Note};
use activitystreams::unparsed::UnparsedMutExt;
use activitystreams_ext::{Ext1, UnparsedExtension};
use serde::{Deserialize, Serialize};

pub type ActorExt = Ext1<ApActor<Person>, PublicKey>;
pub type NoteExt = Ext1<Note, Conversation>;
pub type ArticleExt = Ext1<Article, Conversation>;
pub type ImageExt = Ext1<Image, ImageExtData>;

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyInner {
    pub id: IriString,
    pub owner: IriString,
    pub public_key_pem: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
    pub public_key: PublicKeyInner,
}

impl<U: UnparsedMutExt> UnparsedExtension<U> for PublicKey {
    type Error = serde_json::Error;

    fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
        Ok(PublicKey {
            public_key: unparsed_mut.remove("publicKey")?,
        })
    }

    fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
        unparsed_mut.insert("publicKey", self.public_key)?;
        Ok(())
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Conversation {
    pub conversation: Option<String>,
}

impl Conversation {
    pub fn new() -> Self {
        Self { conversation: None }
    }
}

impl<U: UnparsedMutExt> UnparsedExtension<U> for Conversation {
    type Error = serde_json::Error;

    fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error>
    where
        Self: Sized,
    {
        Ok(Conversation {
            conversation: unparsed_mut.remove("conversation")?,
        })
    }

    fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
        unparsed_mut.insert("conversation", self.conversation)?;
        Ok(())
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ImageExtData {
    pub media_type: String,
    pub url: String,
}

impl ImageExtData {
    pub fn new(media_type: String, url: String) -> Self {
        Self { media_type, url }
    }
}

impl<U: UnparsedMutExt> UnparsedExtension<U> for ImageExtData {
    type Error = serde_json::Error;

    fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error>
    where
        Self: Sized,
    {
        Ok(ImageExtData {
            media_type: unparsed_mut.remove("mediaType")?,
            url: unparsed_mut.remove("url")?,
        })
    }

    fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
        unparsed_mut.insert("mediaType", self.media_type)?;
        unparsed_mut.insert("url", self.url)?;
        Ok(())
    }
}
