pub mod client;
pub mod config;
pub mod helpers;
pub mod serialization;
pub mod types;

use crate::client::LotusClient;

use crate::types::api::RPCRequest;
use crate::types::chain::actor_state::ActorState;
use crate::types::chain::chain_head::ChainHead;
use crate::types::chain::cid::CID;
use crate::types::chain::message::Message;
use crate::types::chain::miner_info::MinerInfo;
use crate::types::chain::network::NetworkVersion;
use crate::types::chain::randomness::DomainSeparationTag;
use crate::types::state::state::State;
use anyhow::Result;
use serde_json::Value::Null;
use serde_json::{json, Value};
use types::chain::epoch::ChainEpoch;
use types::chain::randomness::{Entropy, Randomness};

impl LotusClient {
    pub async fn chain_head(&self) -> Result<ChainHead> {
        self.send("ChainHead".to_string(), vec![]).await
    }

    pub async fn chain_get_tip_set_by_height(&self, height: i64) -> Result<ChainHead> {
        self.send(
            "ChainGetTipSetByHeight".to_string(),
            vec![Value::from(height), Null],
        )
        .await
    }

    pub async fn state_compute(
        &self,
        height: i64,
        messages: Vec<Message>,
        tip_sets: Vec<CID>,
    ) -> Result<State> {
        self.send(
            "StateCompute".to_string(),
            vec![Value::from(height), json!(messages), json!(tip_sets)],
        )
        .await
    }

    pub async fn chain_get_messages_in_tipset(&self, block_cid: CID) -> Result<Vec<Message>> {
        self.send(
            "ChainGetMessagesInTipset".to_string(),
            vec![json!(vec![block_cid])],
        )
        .await
    }

    pub async fn state_decode_params(
        &self,
        to: String,
        method: i64,
        params: String,
    ) -> Result<Value> {
        self.send(
            "StateDecodeParams".to_string(),
            vec![json!(to), json!(method), json!(params), Null],
        )
        .await
    }

    pub async fn state_lookup_robust_address(
        &self,
        address: String,
        ts: Option<CID>,
    ) -> Result<String> {
        self.send(
            "StateLookupRobustAddress".to_string(),
            vec![json!(address), json!(vec![ts])],
        )
        .await
    }

    pub async fn state_get_randomness_from_tickets(
        &self,
        personalization: DomainSeparationTag,
        rand_epoch: ChainEpoch,
        entropy: Entropy,
        tipset_key: Vec<CID>,
    ) -> Result<Randomness> {
        let res: String = self
            .send(
                "StateGetRandomnessFromTickets".to_string(),
                vec![
                    json!(personalization.to_u8()),
                    json!(rand_epoch),
                    json!(entropy),
                    json!(tipset_key),
                ],
            )
            .await?;
        Ok(base64::decode(&res)?)
    }

    pub async fn state_lookup_id(&self, address: String, ts: Option<CID>) -> Result<String> {
        self.send(
            "StateLookupID".to_string(),
            vec![json!(address), json!(vec![ts])],
        )
        .await
    }

    pub async fn state_get_actor(&self, address: String, ts: Option<CID>) -> Result<ActorState> {
        self.send(
            "StateGetActor".to_string(),
            vec![json!(address), json!(vec![ts])],
        )
        .await
    }

    pub async fn chain_get_message(&self, message_cid: CID) -> Result<Message> {
        self.send("ChainGetMessage".to_string(), vec![json!(message_cid)])
            .await
    }

    pub async fn state_miner_info(&self, address: String, ts: Option<CID>) -> Result<MinerInfo> {
        self.send(
            "StateMinerInfo".to_string(),
            vec![json!(address), json!(vec![ts])],
        )
        .await
    }

    pub async fn state_network_version(&self, ts: Option<CID>) -> Result<NetworkVersion> {
        self.send("StateNetworkVersion".to_string(), vec![json!(vec![ts])])
            .await
    }
}
