use std::collections::HashMap;

use serde_json::Value;

use crate::{
    chaindef::{BlockHeader, ScriptHash},
    errors::{RpcError, RpcErrorCode},
};

pub mod communication;
pub mod connection;
pub mod dummycom;
pub mod parse_requests;
pub mod restconnection;
pub mod server;
pub mod ssl_utils;
pub mod tcpcommunication;
pub mod tcpsslcommunication;
pub mod wscommunication;
pub mod wsscommunication;

/// Pre-computed statushash update for a single subscription
#[derive(Debug, Clone)]
pub struct ScriptHashUpdate {
    pub scripthash: ScriptHash,
    pub old_statushash: Option<[u8; 32]>,
    pub new_statushash: Option<[u8; 32]>,
    pub alias: Option<String>,
}

#[derive(Debug)]
pub enum Message {
    Request(String),
    ScriptHashChange(Vec<ScriptHashUpdate>),
    ChainTipChange(Box<BlockHeader>),
    Done,
}

pub enum Notification {
    ScriptHashChange(HashMap<crate::rpc::daemon::server::ConnectionId, Vec<ScriptHashUpdate>>),
    ChainTipChange(Box<BlockHeader>),
    Exit,
}

pub fn to_rpc_error(e: anyhow::Error, id: &Value) -> Value {
    if let Some(rpc_e) = e.downcast_ref::<RpcError>() {
        // Use (at most) two errors from the error chain to produce
        // an error descrption.
        let errmsgs: Vec<String> = e.chain().take(2).map(|x| x.to_string()).collect();
        let errmsgs = errmsgs.join("; ");
        json!({"jsonrpc": "2.0",
        "id": id,
        "error": {
            "code": rpc_e.code as i32,
            "message": errmsgs,
        }})
    } else {
        json!({"jsonrpc": "2.0",
        "id": id,
        "error": {
            "code": RpcErrorCode::InternalError as i32,
            "message": e.to_string()
        }})
    }
}
