// {{ display_name }} 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 {{ crate_name }}::core::config_schema::{AuthMethod, Transport};
use {{ crate_name }}::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("{{ display_name }} 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> {
    {%- if auth_methods | length == 1 %}
    // Exactly one scheme was discovered — nothing to choose between.
    Ok(AuthMethod::{{ auth_methods.0.variant_name }})
    {%- else %}
    let choices = vec![{% for method in auth_methods %}"{{ method.key }}"{% if not loop.last %}, {% endif %}{% endfor %}];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("Authentication method:", choices).prompt()
    })
    .await??;

    Ok(match selection {
    {%- for method in auth_methods %}
        "{{ method.key }}" => AuthMethod::{{ method.variant_name }},
    {%- endfor %}
        other => anyhow::bail!("unexpected auth method selection '{other}'"),
    })
    {%- endif %}
}

// mcpify:versions:begin
async fn prompt_api_version() -> anyhow::Result<String> {
    {%- if version_entries | length == 1 %}
    Ok("{{ version_entries.0.label }}".to_string())
    {%- else %}
    let choices = vec![
    {%- for v in version_entries %}
        "{{ v.label }}{% if v.label == default_version_label %} (default/latest){% endif %}",
    {%- endfor %}
    ];
    let selection = tokio::task::spawn_blocking(move || {
        inquire::Select::new("API version to use:", choices).prompt()
    })
    .await??;

    Ok(match selection {
    {%- for v in version_entries %}
        "{{ v.label }}{% if v.label == default_version_label %} (default/latest){% endif %}" => "{{ v.label }}".to_string(),
    {%- endfor %}
        other => other.to_string(),
    })
    {%- endif %}
}
// 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 {
            {%- if "basic" in auth_method_keys %}
            AuthMethod::Basic => {
                credentials.insert(
                    "username".to_string(),
                    inquire::Text::new("Username:").prompt()?,
                );
                credentials.insert(
                    "password".to_string(),
                    inquire::Password::new("Password:")
                        .without_confirmation()
                        .prompt()?,
                );
            }
            {%- endif %}
            {%- if "pat" in auth_method_keys %}
            AuthMethod::Pat => {
                credentials.insert(
                    "token".to_string(),
                    inquire::Password::new("Personal access token:")
                        .without_confirmation()
                        .prompt()?,
                );
            }
            {%- endif %}
            {%- if "apiKey" in auth_method_keys %}
            AuthMethod::ApiKey => {
                credentials.insert(
                    "api_key".to_string(),
                    inquire::Password::new("API key:")
                        .without_confirmation()
                        .prompt()?,
                );
            }
            {%- endif %}
            {%- if "oauth2" in auth_method_keys %}
            AuthMethod::OAuth2 => {
                credentials.insert(
                    "client_id".to_string(),
                    inquire::Text::new("OAuth2 client ID:").prompt()?,
                );
                let client_secret =
                    inquire::Password::new("OAuth2 client secret (leave blank if none):")
                        .without_confirmation()
                        .prompt()?;
                if !client_secret.is_empty() {
                    credentials.insert("client_secret".to_string(), client_secret);
                }
                credentials.insert(
                    "token_url".to_string(),
                    inquire::Text::new("OAuth2 token URL:").prompt()?,
                );
                let authorization_code = inquire::Text::new(
                    "Authorization code, if you already have one (leave blank to skip):",
                )
                .prompt()?;
                if !authorization_code.is_empty() {
                    credentials.insert("authorization_code".to_string(), authorization_code);
                }
            }
            {%- endif %}
            {%- if "oauth1" in auth_method_keys %}
            AuthMethod::OAuth1 => {
                credentials.insert(
                    "consumer_key".to_string(),
                    inquire::Text::new("OAuth1 consumer key:").prompt()?,
                );
                let private_key_path =
                    inquire::Text::new("Path to RSA private key file:").prompt()?;
                let private_key = std::fs::read_to_string(&private_key_path)
                    .map_err(|err| anyhow::anyhow!("failed to read '{private_key_path}': {err}"))?;
                credentials.insert("private_key".to_string(), private_key);
            }
            {%- endif %}
        }
        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!("{{ project_name }} 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": {
                    "{{ project_name }}": {
                        "command": "{{ project_name }}",
                        "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: `{{ project_name }} start {cli_args}`)");
        }
        Transport::Http => {
            {%- if "oauth1" in auth_method_keys %}
            if auth_method == AuthMethod::OAuth1 {
                println!(
                    "\nOAuth1 is not supported over HTTP transport: its signature must be \
                     recomputed per request against the destination URL using the server's \
                     consumer key/secret, which a simple incoming-header relay cannot \
                     provide. Use stdio transport for this API instead."
                );
                return;
            }
            {%- endif %}
            let (_, header_name) =
                {{ crate_name }}::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": {
                    "{{ project_name }}": {
                        "url": "http://127.0.0.1:3000/mcp",
                        "headers": headers,
                    }
                }
            });
            println!(
                "\nMCP client config (http) — adjust the host/port to match how you run \
                 `{{ project_name }} 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("{{ tool_prefix_env }}_URL".to_string(), url);
    env.insert(
        "{{ tool_prefix_env }}_AUTH_METHOD".to_string(),
        serde_json::to_value(auth_method)?
            .as_str()
            .unwrap_or_default()
            .to_string(),
    );
    env.insert("{{ tool_prefix_env }}_API_VERSION".to_string(), api_version);
    env.insert(
        "{{ tool_prefix_env }}_TRANSPORT".to_string(),
        serde_json::to_value(transport)?
            .as_str()
            .unwrap_or_default()
            .to_string(),
    );
    for (key, value) in &credentials {
        env.insert(format!("{{ tool_prefix_env }}_{}", 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: {{ project_name }} {run_command}");
    Ok(())
}
