use clap::{Parser, Subcommand};
use flaredb_client::RdbClient;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Args {
    #[arg(long, default_value = "127.0.0.1:50052")]
    addr: String,

    #[arg(long, default_value = "127.0.0.1:2479")]
    pd_addr: String,

    #[arg(long, default_value = "")]
    namespace: String,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    RawPut {
        #[arg(long)]
        key: String,
        #[arg(long)]
        value: String,
    },
    RawGet {
        #[arg(long)]
        key: String,
    },
    CasGet {
        #[arg(long)]
        key: String,
    },
    Cas {
        #[arg(long)]
        key: String,
        #[arg(long)]
        value: String,
        #[arg(long)]
        expected: u64,
    },
    Tso,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    let mut client =
        RdbClient::connect_with_pd_namespace(args.addr, args.pd_addr, args.namespace).await?;

    match args.command {
        Commands::RawPut { key, value } => {
            client.raw_put(key.into_bytes(), value.into_bytes()).await?;
            println!("Success");
        }
        Commands::RawGet { key } => match client.raw_get(key.into_bytes()).await? {
            Some(val) => println!("{}", String::from_utf8_lossy(&val)),
            None => {
                eprintln!("Not found");
                std::process::exit(1);
            }
        },
        Commands::Cas {
            key,
            value,
            expected,
        } => {
            let (success, current, new) = client
                .cas(key.into_bytes(), value.into_bytes(), expected)
                .await?;
            if success {
                println!("Success, Version: {}", new);
            } else {
                println!("Conflict! Current Version: {}", current);
            }
        }
        Commands::Tso => {
            let ts = client.get_tso().await?;
            println!("Timestamp: {}", ts);
        }
        Commands::CasGet { key } => match client.cas_get(key.into_bytes()).await? {
            Some((ver, val)) => {
                println!("Version: {}, Value: {}", ver, String::from_utf8_lossy(&val));
            }
            None => {
                eprintln!("Not found");
                std::process::exit(1);
            }
        },
    }

    Ok(())
}
