// Bitbucket Data Center MCP server — generated by mcpify. Do not hand-edit.
//
// Interactive setup wizard (REQ-1.6): collects the API URL and the
// credentials the discovered auth scheme(s) need, then persists them per
// the operator's choice: a .env file, a config.json file, or a
// ready-to-run CLI invocation printed to stdout (nothing written to disk).
// `inquire`'s prompts are blocking — run through `spawn_blocking`, the
// same pattern mcpify's own `auth_profile::prompt` documents for calling
// it from an async runtime.

use std::collections::HashMap;
use std::time::Duration;

use bitbucket_dc_mcp::core::config_schema::{AuthMethod, Transport};
use bitbucket_dc_mcp::core::credential_storage::save_credential;

fn to_env_key(key: &str) -> String {
    key.to_uppercase()
}

async fn prompt_base_url() -> anyhow::Result<String> {
    let url = tokio::task::spawn_blocking(|| {
        inquire::Text::new("Bitbucket Data Center base URL:").prompt()
    })
    .await??;

    let client = reqwest::Client::new();
    match client
        .head(&url)
        .timeout(Duration::from_secs(5))
        .send()
        .await
    {
        Ok(_) => println!("reachable"),
        Err(_) => println!(
            "could not reach that URL yet — continuing anyway (fix it later with `test-connection`)"
        ),
    }

    Ok(url)
}

async fn prompt_auth_method() -> anyhow::Result<AuthMethod> {
    // The OpenAPI spec only documents Basic auth, but Data Center deployments
    // also accept a Personal Access Token bearer — offer both.
    let choices = vec![
        "Personal Access Token (recommended)",
        "Basic (username + password or app token)",
    ];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("Which auth method should this deployment use?", choices).prompt()
    })
    .await??;

    Ok(if selection.starts_with("Personal Access Token") {
        AuthMethod::Pat
    } else {
        AuthMethod::Basic
    })
}

// mcpify:versions:begin
async fn prompt_api_version() -> anyhow::Result<String> {
    let choices = vec![
        "10.3 (default/latest)",
        "10.2",
        "10.0",
        "9.6",
        "9.5",
        "9.4",
        "9.3",
        "9.2",
        "9.1",
        "9.0",
        "8.19",
        "8.18",
        "8.17",
        "8.16",
        "8.15",
        "8.14",
        "8.13",
        "8.12",
        "8.11",
        "8.10",
        "8.9",
        "8.8",
        "8.7",
        "8.6",
        "8.5",
        "8.4",
        "8.3",
        "8.2",
        "8.1",
        "8.0",
    ];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("API version to use:", choices).prompt()
    })
    .await??;

    Ok(match selection {
        "10.3 (default/latest)" => "10.3".to_string(),
        "10.2" => "10.2".to_string(),
        "10.0" => "10.0".to_string(),
        "9.6" => "9.6".to_string(),
        "9.5" => "9.5".to_string(),
        "9.4" => "9.4".to_string(),
        "9.3" => "9.3".to_string(),
        "9.2" => "9.2".to_string(),
        "9.1" => "9.1".to_string(),
        "9.0" => "9.0".to_string(),
        "8.19" => "8.19".to_string(),
        "8.18" => "8.18".to_string(),
        "8.17" => "8.17".to_string(),
        "8.16" => "8.16".to_string(),
        "8.15" => "8.15".to_string(),
        "8.14" => "8.14".to_string(),
        "8.13" => "8.13".to_string(),
        "8.12" => "8.12".to_string(),
        "8.11" => "8.11".to_string(),
        "8.10" => "8.10".to_string(),
        "8.9" => "8.9".to_string(),
        "8.8" => "8.8".to_string(),
        "8.7" => "8.7".to_string(),
        "8.6" => "8.6".to_string(),
        "8.5" => "8.5".to_string(),
        "8.4" => "8.4".to_string(),
        "8.3" => "8.3".to_string(),
        "8.2" => "8.2".to_string(),
        "8.1" => "8.1".to_string(),
        "8.0" => "8.0".to_string(),
        other => other.to_string(),
    })
}
// mcpify:versions:end

async fn prompt_transport() -> anyhow::Result<Transport> {
    let choices = vec![
        "stdio (spawned as a subprocess by the MCP client)",
        "http (a standalone server the MCP client connects to over the network)",
    ];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("Which transport will this deployment use?", choices).prompt()
    })
    .await??;

    Ok(if selection.starts_with("stdio") {
        Transport::Stdio
    } else {
        Transport::Http
    })
}

async fn prompt_credentials(auth_method: AuthMethod) -> anyhow::Result<HashMap<String, String>> {
    tokio::task::spawn_blocking(move || -> anyhow::Result<HashMap<String, String>> {
        let mut credentials = HashMap::new();
        match auth_method {
            AuthMethod::Basic => {
                credentials.insert(
                    "username".to_string(),
                    inquire::Text::new("Username:").prompt()?,
                );
                credentials.insert(
                    "password".to_string(),
                    inquire::Password::new(
                        "Password (or app/access token, if your account uses one):",
                    )
                    .without_confirmation()
                    .prompt()?,
                );
            }
            AuthMethod::Pat => {
                credentials.insert(
                    "token".to_string(),
                    inquire::Password::new("Personal Access Token:")
                        .without_confirmation()
                        .prompt()?,
                );
            }
        }
        Ok(credentials)
    })
    .await?
}

async fn prompt_persistence(env: &HashMap<String, String>) -> anyhow::Result<()> {
    let choices = vec![
        "Write a .env file",
        "Write a config.json file",
        "Print a ready-to-run CLI invocation (nothing written to disk)",
    ];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("How should these settings be saved?", choices).prompt()
    })
    .await??;

    match selection {
        "Write a .env file" => {
            let contents = env
                .iter()
                .map(|(key, value)| format!("{key}={value}"))
                .collect::<Vec<_>>()
                .join("\n");
            std::fs::write(".env", format!("{contents}\n"))?;
            println!("Wrote .env");
        }
        "Write a config.json file" => {
            std::fs::write("config.json", serde_json::to_string_pretty(env)?)?;
            println!("Wrote config.json");
        }
        _ => {
            let flags = env
                .iter()
                .map(|(key, value)| {
                    format!("--{} {:?}", key.to_lowercase().replace('_', "-"), value)
                })
                .collect::<Vec<_>>()
                .join(" ");
            println!("bitbucket-dc-mcp start {flags}");
        }
    }
    Ok(())
}

/// Prints a ready-to-use MCP client config (the `mcpServers` block a host
/// like Claude Code/Desktop expects) for whichever transport was selected
/// — the stdio and http shapes are mutually exclusive, never both, since
/// they're two different ways of running this same server and a client
/// only ever picks one. HTTP transport shows credentials as headers only
/// (never env/CLI args, matching `AuthManager::apply_auth_headers`'s
/// HTTP-only-uses-per-request-credentials behavior); stdio shows both the
/// `env` block and the equivalent all-CLI-args invocation, since the
/// config cascade accepts either for a spawned subprocess.
fn print_mcp_client_config(
    transport: Transport,
    auth_method: AuthMethod,
    env: &HashMap<String, String>,
) {
    match transport {
        Transport::Stdio => {
            let config = serde_json::json!({
                "mcpServers": {
                    "bitbucket-dc-mcp": {
                        "command": "bitbucket-dc-mcp",
                        "args": ["start"],
                        "env": env,
                    }
                }
            });
            println!("\nMCP client config (stdio):");
            println!("{}", serde_json::to_string_pretty(&config).unwrap());

            let cli_args = env
                .iter()
                .map(|(key, value)| format!("--{} {value:?}", key.to_lowercase().replace('_', "-")))
                .collect::<Vec<_>>()
                .join(" ");
            println!("(equivalently: `bitbucket-dc-mcp start {cli_args}`)");
        }
        Transport::Http => {
            let (_, header_name) =
                bitbucket_dc_mcp::auth::auth_manager::header_location_for(auth_method);
            let mut headers = serde_json::Map::new();
            headers.insert(
                header_name.to_string(),
                serde_json::Value::String("<credential value here>".to_string()),
            );
            let config = serde_json::json!({
                "mcpServers": {
                    "bitbucket-dc-mcp": {
                        "url": "http://127.0.0.1:3000/mcp",
                        "headers": headers,
                    }
                }
            });
            println!(
                "\nMCP client config (http) — adjust the host/port to match how you run \
                 `bitbucket-dc-mcp http`:"
            );
            println!("{}", serde_json::to_string_pretty(&config).unwrap());
            println!(
                "Credentials are never read from local config/env over HTTP transport — \
                 every request must carry its own \"{header_name}\" header."
            );
        }
    }
}

pub async fn run_setup_wizard() -> anyhow::Result<()> {
    let url = prompt_base_url().await?;
    let auth_method = prompt_auth_method().await?;
    let credentials = prompt_credentials(auth_method).await?;
    let api_version = prompt_api_version().await?;
    let transport = prompt_transport().await?;

    save_credential("active-credentials", &serde_json::to_string(&credentials)?)?;

    let mut env: HashMap<String, String> = HashMap::new();
    env.insert("BITBUCKET_DC_MCP_URL".to_string(), url);
    env.insert(
        "BITBUCKET_DC_MCP_AUTH_METHOD".to_string(),
        serde_json::to_value(auth_method)?
            .as_str()
            .unwrap_or_default()
            .to_string(),
    );
    env.insert("BITBUCKET_DC_MCP_API_VERSION".to_string(), api_version);
    env.insert(
        "BITBUCKET_DC_MCP_TRANSPORT".to_string(),
        serde_json::to_value(transport)?
            .as_str()
            .unwrap_or_default()
            .to_string(),
    );
    for (key, value) in &credentials {
        env.insert(
            format!("BITBUCKET_DC_MCP_{}", to_env_key(key)),
            value.clone(),
        );
    }

    prompt_persistence(&env).await?;
    print_mcp_client_config(transport, auth_method, &env);

    let run_command = match transport {
        Transport::Stdio => "start",
        Transport::Http => "http",
    };
    println!("Setup complete! Run: bitbucket-dc-mcp {run_command}");
    Ok(())
}
