use crate::args::*;
use shared::core::ChainData;
use crate::executor::ExecutorCommand;
use crate::node::*;
use cli_renderer::RenderCommand;
use clap::Parser;

pub fn handle_peer_command(cmd: CliPeerCommand) -> NodeCommand {
  match cmd {
    CliPeerCommand::List => NodeCommand::ListPeers,
    CliPeerCommand::Remove { id } => NodeCommand::RemovePeer {
      peer_id: id.parse::<uuid::Uuid>().unwrap(),
    },
    CliPeerCommand::Connect { addr } => NodeCommand::ConnectTcpPeer(addr),
  }
}

pub fn handle_block_command(cmd: CliBlockCommand) -> NodeCommand {
  match cmd {
    CliBlockCommand::List => NodeCommand::ListBlocks,
    CliBlockCommand::Create => NodeCommand::CreateBlock,
    CliBlockCommand::Display { key, height } => match (key, height) {
      (Some(k), _) => return NodeCommand::DisplayBlockByKey(k),
      (_, Some(h)) => return NodeCommand::DisplayBlockByHeight(h),
      (None, None) => return NodeCommand::DisplayBlockInteractive,
    },
  }
}

fn handle_seed_command(cmd: CliSeedCommand) -> NodeCommand {
  match cmd {
    CliSeedCommand::Connect => NodeCommand::ConnectToSeeds,
  }
}

fn handle_ping(cmd: CliPingCommand) -> NodeCommand {
  match cmd {
    CliPingCommand::Id { id } => NodeCommand::PingId(id),
    CliPingCommand::Addr { addr } => NodeCommand::PingAddr(addr),
  }
}

pub fn cli(input: &str) -> ExecutorCommand {
  let argv: Vec<&str> = std::iter::once(" ")
    .chain(input.split_whitespace())
    .collect();
  match Cli::try_parse_from(argv) {
    Ok(cmd) => match cmd.command {
      CliCommand::Layout { mode } => ExecutorCommand::Render(RenderCommand::ChangeLayout(mode)),
      CliCommand::Clear => ExecutorCommand::Render(RenderCommand::ClearPane),
      CliCommand::Peer { peer_cmd } => ExecutorCommand::Node(handle_peer_command(peer_cmd)),
      CliCommand::Block { block_cmd } => ExecutorCommand::Node(handle_block_command(block_cmd)),
      CliCommand::Transaction(tx) => {
        ExecutorCommand::Node(NodeCommand::ProcessChainData(ChainData::Transaction(tx)))
      }
      CliCommand::Award { address, amount } => {ExecutorCommand::Node(NodeCommand::AwardCurrency{ address, amount })}
      CliCommand::DebugShowId => ExecutorCommand::Node(NodeCommand::ShowId),
      CliCommand::StartListner { addr } => {
        ExecutorCommand::Node(NodeCommand::StartListner(addr.parse().unwrap()))
      }
      CliCommand::Seeds { seed_cmd } => ExecutorCommand::Node(handle_seed_command(seed_cmd)),
      CliCommand::Ping { ping_cmd } => ExecutorCommand::Node(handle_ping(ping_cmd)),
    },
    Err(e) => ExecutorCommand::InvalidCommand(format!("{e}")),
  }
}
