//! OK
//!
//! When submitting events to relays, clients currently have no way to know if
//! an event was successfully committed to the database. This NIP introduces the
//! concept of command results which are like NOTICE's except provide more
//! information about if an event was accepted or rejected.
use crate::{
    common::SerializeDeserialize,
    types::{self, Addr},
};
use serde_json::Value;
use std::collections::VecDeque;

/// OK messages
#[derive(Clone, Debug)]
pub struct ROk {
    /// Event Id
    pub id: Addr,
    /// Whether the event has been successful or not
    pub status: bool,
    /// Some message
    pub message: String,
}

impl SerializeDeserialize for ROk {
    fn get_tag() -> &'static str {
        "OK"
    }

    fn serialize(&self) -> Result<Vec<Value>, String> {
        Ok(vec![
            Value::String(Self::get_tag().to_owned()),
            Value::String(self.id.to_hex()),
            Value::Bool(self.status),
            Value::String(self.message.clone()),
        ])
    }

    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
        if args.len() < 3 {
            return Err("Invalid length".to_owned());
        }
        let id = args[0]
            .as_str()
            .ok_or_else(|| "Invalid type for element 1, expecting a string".to_owned())?
            .try_into()
            .map_err(|e: types::addr::Error| format!("Invalid id: {}", e))?;
        let status = args[1].as_bool().ok_or("Invalid type, expected bool")?;
        let message = args[2]
            .as_str()
            .ok_or_else(|| "Invalid message, expected string".to_owned())?
            .to_owned();
        Ok(Self {
            id,
            status,
            message,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::Response;

    #[test]
    fn parse() {
        let json = r#"["OK", "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", true, "pow: difficulty 25>=24"]"#;
        let message: Response = serde_json::from_str(json).expect("message");
        assert!(message.as_ok().is_some());
    }

    #[test]
    fn serialize() {
        let ok_ = ROk {
            id: "a0b0".try_into().expect("valid_id"),
            status: true,
            message: "Some test".into(),
        };
        let m: Response = ok_.into();
        assert_eq!(
            r#"["OK","a0b0",true,"Some test"]"#,
            serde_json::to_string(&m).expect("valid json")
        );
    }
}
