use crate::{
    ApAccept,
    ApAdd,
    ApAnnounce,
    ApBlock,
    ApCreate,
    ApDelete,
    ApFollow,
    ApLike,
    ApObject,
    //ApRemove,
    ApUndo,
    ApUpdate,
};
use anyhow::Result;
use jdt_maybe_multiple::MaybeMultiple;
use jdt_maybe_reference::MaybeReference;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt::Debug;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum ApActivity {
    Delete(Box<ApDelete>),
    Like(Box<ApLike>),
    Undo(Box<ApUndo>),
    Accept(Box<ApAccept>),
    Follow(ApFollow),
    Announce(ApAnnounce),
    Create(ApCreate),
    Update(ApUpdate),
    Block(ApBlock),
    Add(ApAdd),
    //Remove(ApRemove),
}

impl std::fmt::Display for ApActivity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ApActivity::Delete(delete) => write!(f, "{}", delete),
            ApActivity::Like(like) => write!(f, "{}", like),
            ApActivity::Undo(undo) => write!(f, "{}", undo),
            ApActivity::Accept(accept) => write!(f, "{}", accept),
            ApActivity::Follow(follow) => write!(f, "{}", follow),
            ApActivity::Announce(announce) => write!(f, "{}", announce),
            ApActivity::Create(create) => write!(f, "{}", create),
            ApActivity::Update(update) => write!(f, "{}", update),
            ApActivity::Block(block) => write!(f, "{}", block),
            ApActivity::Add(add) => write!(f, "{}", add),
        }
    }
}

impl ApActivity {
    pub fn is_delete(&self) -> bool {
        matches!(self, ApActivity::Delete(_))
    }

    pub fn is_like(&self) -> bool {
        matches!(self, ApActivity::Like(_))
    }

    pub fn is_undo(&self) -> bool {
        matches!(self, ApActivity::Undo(_))
    }

    pub fn is_accept(&self) -> bool {
        matches!(self, ApActivity::Accept(_))
    }

    pub fn is_follow(&self) -> bool {
        matches!(self, ApActivity::Follow(_))
    }

    pub fn is_announce(&self) -> bool {
        matches!(self, ApActivity::Announce(_))
    }

    pub fn is_create(&self) -> bool {
        matches!(self, ApActivity::Create(_))
    }

    pub fn is_update(&self) -> bool {
        matches!(self, ApActivity::Update(_))
    }

    pub fn is_block(&self) -> bool {
        matches!(self, ApActivity::Block(_))
    }

    pub fn is_add(&self) -> bool {
        matches!(self, ApActivity::Add(_))
    }
}

impl From<ApAnnounce> for ApActivity {
    fn from(announce: ApAnnounce) -> Self {
        ApActivity::Announce(announce)
    }
}

impl From<ApCreate> for ApActivity {
    fn from(create: ApCreate) -> Self {
        ApActivity::Create(create)
    }
}

impl From<Box<ApUndo>> for ApActivity {
    fn from(undo: Box<ApUndo>) -> Self {
        ApActivity::Undo(undo)
    }
}

impl From<ApUpdate> for ApActivity {
    fn from(update: ApUpdate) -> Self {
        ApActivity::Update(update)
    }
}

impl From<Box<ApDelete>> for ApActivity {
    fn from(delete: Box<ApDelete>) -> Self {
        ApActivity::Delete(delete)
    }
}

impl From<ApFollow> for ApActivity {
    fn from(follow: ApFollow) -> Self {
        ApActivity::Follow(follow)
    }
}

impl From<Box<ApLike>> for ApActivity {
    fn from(like: Box<ApLike>) -> Self {
        ApActivity::Like(like)
    }
}

impl ApActivity {
    pub fn as_id(&self) -> Option<String> {
        match self {
            ApActivity::Like(like) => like.id.clone(),
            ApActivity::Follow(follow) => follow.id.clone(),
            ApActivity::Announce(announce) => announce.id.clone(),
            ApActivity::Delete(delete) => delete.id.clone(),
            ApActivity::Undo(undo) => undo.id.clone(),
            ApActivity::Accept(accept) => accept.id.clone(),
            ApActivity::Create(create) => create.id.clone(),
            ApActivity::Update(update) => update.id.clone(),
            ApActivity::Block(block) => block.id.clone(),
            _ => None,
        }
    }

    pub fn is_signed(&self) -> bool {
        match self {
            ApActivity::Like(_like) => false,
            ApActivity::Follow(_follow) => false,
            ApActivity::Announce(_announce) => false,
            ApActivity::Delete(delete) => delete.signature.is_some(),
            ApActivity::Undo(_undo) => false,
            ApActivity::Accept(_accept) => false,
            ApActivity::Create(create) => create.signature.is_some(),
            ApActivity::Update(update) => update.signature.is_some(),
            ApActivity::Block(_block) => false,
            _ => false,
        }
    }

    pub fn formalize(&self) -> Self {
        match self.clone() {
            ApActivity::Announce(mut announce) => {
                announce.ephemeral = None;
                if let MaybeReference::Actual(ApObject::Note(ref mut note)) = announce.object {
                    note.context = None;
                    note.ephemeral = None;
                    note.instrument = MaybeMultiple::None;
                }
                announce.into()
            }
            ApActivity::Create(mut create) => {
                create.ephemeral = None;
                if let MaybeReference::Actual(ApObject::Note(ref mut note)) = create.object {
                    note.context = None;
                    note.ephemeral = None;
                    note.instrument = MaybeMultiple::None;
                }
                create.into()
            }
            _ => self.clone(),
        }
    }
}

impl TryFrom<Value> for ApActivity {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<ApActivity> {
        serde_json::from_value(value).map_err(anyhow::Error::msg)
    }
}
