use bft_crdt::websocket;
use bft_crdt::TransactionList;
use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp};
use cli::{parse_args, Commands};
use node::SideNode;
use tokio::{sync::mpsc, task};

pub mod bft_crdt;
pub mod bitcoin;
pub(crate) mod cli;
pub(crate) mod init;
pub mod node;
pub mod utils;

#[tokio::main]
pub async fn run() {
    let args = parse_args();

    match &args.command {
        Some(Commands::Init { name }) => {
            let config = init::config::SideNodeConfig {
                name: name.to_string(),
            };

            let _ = init::init(utils::home(name), config);
        }
        Some(Commands::Run { name }) => {
            let mut node = setup(name).await;
            node.start().await;
        }
        Some(Commands::BtcTransfer {}) => {
            let _ = bitcoin::driver::simple_transfer::run().await;
        }

        Some(Commands::BtcTransferPolicy {}) => {
            let _ = bitcoin::driver::transfer_policy::run().await;
        }

        Some(Commands::BtcHtlc {}) => {
            let _ = bitcoin::driver::create_htlc::run().await;
        }

        None => println!("No command provided. Exiting. See --help for more information."),
    }
}

/// Wire everything up outside the application so that we can test more easily later
async fn setup(name: &String) -> SideNode {
    // First, load up the keys and create a bft-bft-crdt
    let side_dir = utils::home(name);
    let bft_crdt_keys = bft_crdt::keys::load_from_file(&side_dir);
    // let keys = bitcoin::keys::load_from_file(&side_dir).unwrap();
    // let bitcoin_wallet =
    //     bitcoin::clients::esplora::EsploraWallet::create_wallet(name, keys).unwrap();
    let crdt = BaseCrdt::<TransactionList>::new(&bft_crdt_keys);

    // Channels for internal communication, and a tokio task for stdin input
    let (incoming_sender, incoming_receiver) = mpsc::channel::<SignedOp>(32);
    let (stdin_sender, stdin_receiver) = std::sync::mpsc::channel();
    task::spawn(async move {
        bft_crdt::stdin::input(stdin_sender);
    });

    // Finally, create the node and return it
    let handle = websocket::Client::new(incoming_sender).await;
    let node = SideNode::new(
        crdt,
        bft_crdt_keys,
        // bitcoin_wallet,
        incoming_receiver,
        stdin_receiver,
        handle,
    );
    println!("Node setup complete.");
    node
}
