use crate::operator_cli::types::{OperatorCommand, S3ServeStorage};
use crate::s3_listener::{DEFAULT_CONSOLE_BIND, DEFAULT_DATA_DIR};
use std::str;

pub(super) fn parse_s3_serve_command(
    args: &[String],
    global_config_path: Option<&str>,
) -> Result<OperatorCommand, String> {
    let mut bind = "127.0.0.1:9000".to_string();
    let mut console_bind = DEFAULT_CONSOLE_BIND.to_string();
    let mut config_path = global_config_path.map(str::to_string);
    let mut principal = "boto3".to_string();
    let mut access_key_id = "AKIDEXAMPLE".to_string();
    let mut secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY".to_string();
    let mut storage_mode = "filesystem".to_string();
    let mut data_dir: Option<String> = None;

    let mut index = 0;
    while index < args.len() {
        match args[index].as_str() {
            "--bind" => {
                index += 1;
                bind = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--bind`".to_string())?;
            }
            "--console-bind" => {
                index += 1;
                console_bind = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--console-bind`".to_string())?;
            }
            "--config" => {
                index += 1;
                config_path = Some(
                    args.get(index)
                        .cloned()
                        .ok_or_else(|| "missing value for `--config`".to_string())?,
                );
            }
            "--principal" => {
                index += 1;
                principal = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--principal`".to_string())?;
            }
            "--access-key" => {
                index += 1;
                access_key_id = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--access-key`".to_string())?;
            }
            "--secret-key" => {
                index += 1;
                secret_access_key = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--secret-key`".to_string())?;
            }
            "--storage" => {
                index += 1;
                storage_mode = args
                    .get(index)
                    .cloned()
                    .ok_or_else(|| "missing value for `--storage`".to_string())?;
                if storage_mode != "filesystem" && storage_mode != "in-memory" {
                    return Err(format!(
                        "invalid s3 serve storage mode `{storage_mode}`; expected `filesystem` or `in-memory`"
                    ));
                }
            }
            "--data-dir" => {
                index += 1;
                data_dir = Some(
                    args.get(index)
                        .cloned()
                        .ok_or_else(|| "missing value for `--data-dir`".to_string())?,
                );
            }
            "--in-memory" => {
                storage_mode = "in-memory".to_string();
            }
            other => return Err(format!("unknown s3 serve flag `{other}`")),
        }
        index += 1;
    }

    Ok(OperatorCommand::S3Serve {
        bind,
        console_bind,
        config_path,
        principal,
        access_key_id,
        secret_access_key,
        storage: parse_s3_storage(storage_mode, data_dir)?,
    })
}

fn parse_s3_storage(
    storage_mode: String,
    data_dir: Option<String>,
) -> Result<S3ServeStorage, String> {
    match storage_mode.as_str() {
        "filesystem" => Ok(S3ServeStorage::Filesystem {
            data_dir: data_dir.unwrap_or_else(|| DEFAULT_DATA_DIR.to_string()),
        }),
        "in-memory" => {
            if data_dir.is_some() {
                return Err("cannot use `--data-dir` with in-memory s3 serve storage".to_string());
            }
            Ok(S3ServeStorage::InMemory)
        }
        _ => unreachable!("storage mode is validated when parsed"),
    }
}
