extern crate snowflake_api;

use anyhow::Result;
use arrow::util::pretty::pretty_format_batches;
use clap::Parser;
use std::fs;

use snowflake_api::{QueryResult, SnowflakeApi};

#[derive(clap::ValueEnum, Clone, Debug)]
enum Output {
    Arrow,
    Json,
    Query,
}

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    /// Path to RSA PEM private key
    #[arg(long)]
    private_key: Option<String>,

    /// Password if certificate is not present
    #[arg(long)]
    password: Option<String>,

    /// <account_identifier> in Snowflake format, uppercase
    #[arg(short, long)]
    account_identifier: String,

    /// Database name
    #[arg(short, long)]
    database: String,

    /// Schema name
    #[arg(long)]
    schema: String,

    /// Warehouse
    #[arg(short, long)]
    warehouse: String,

    /// username to whom the private key belongs to
    #[arg(short, long)]
    username: String,

    /// role which user will assume
    #[arg(short, long)]
    role: String,

    /// sql statement to execute and print result from
    #[arg(long)]
    sql: String,

    #[arg(long)]
    #[arg(value_enum, default_value_t = Output::Arrow)]
    output: Output,
}

#[tokio::main]
async fn main() -> Result<()> {
    env_logger::init();

    let args = Args::parse();

    let mut api = match (&args.private_key, &args.password) {
        (Some(pkey), None) => {
            let pem = fs::read(pkey)?;
            SnowflakeApi::with_certificate_auth(
                &args.account_identifier,
                &args.warehouse,
                Some(&args.database),
                Some(&args.schema),
                &args.username,
                Some(&args.role),
                &pem,
            )?
        }
        (None, Some(pwd)) => SnowflakeApi::with_password_auth(
            &args.account_identifier,
            &args.warehouse,
            Some(&args.database),
            Some(&args.schema),
            &args.username,
            Some(&args.role),
            pwd,
        )?,
        _ => {
            panic!("Either private key path or password must be set")
        }
    };

    match args.output {
        Output::Arrow => {
            let res = api.exec(&args.sql).await?;
            match res {
                QueryResult::Arrow(a) => {
                    println!("{}", pretty_format_batches(&a).unwrap());
                }
                QueryResult::Json(j) => {
                    println!("{}", j.to_string());
                }
                QueryResult::Empty => {
                    println!("Query finished successfully")
                }
            }
        }
        Output::Json => {
            let res = api.exec_json(&args.sql).await?;
            println!("{}", res.to_string());
        }
        Output::Query => {
            let res = api.exec_response(&args.sql).await?;
            println!("{:?}", res);
        }
    }

    Ok(())
}
