//! Handler for `mcptest init`.

use anyhow::Result;

use crate::cli::args::{InitArgs, InitAuthKind};
use crate::cli::GlobalArgs;
use crate::commands;

/// Handle `mcptest init <flags>` by delegating to the scaffolder.
pub(crate) fn init_command(args: &InitArgs, global: &GlobalArgs) -> Result<i32> {
    let cwd = std::env::current_dir()?;
    let discovered = match build_discovered_target(args) {
        Ok(t) => t,
        Err(err) => {
            eprintln!("mcptest init: {err}");
            return Ok(2);
        }
    };
    let url_target = match build_url_target(args) {
        Ok(t) => t,
        Err(err) => {
            eprintln!("mcptest init: {err}");
            return Ok(2);
        }
    };
    commands::run_init(
        &cwd,
        args.with_jury,
        args.force,
        global.quiet,
        url_target,
        discovered,
    )
}

/// Resolve `--from-discovered <NAME>` against the local client-config walk.
///
/// Returns `Ok(None)` when the flag is absent. Returns `Err(...)` (exit 2) when
/// the name matches no discovered server, listing the names that are available
/// so the operator can correct the typo. Only the env-var names are carried
/// into the scaffold, never their values.
pub(crate) fn build_discovered_target(
    args: &InitArgs,
) -> Result<Option<commands::DiscoveredTarget>, String> {
    let Some(name) = args.from_discovered.clone() else {
        return Ok(None);
    };
    let servers = mcptest_core::discovery::clients::discover_all_clients();
    let Some(found) = servers.into_iter().find(|s| s.name == name) else {
        return Err(format!(
            "no discovered server named `{name}`. Run `mcptest doctor` to see the servers \
             found in your local client configs."
        ));
    };
    Ok(Some(commands::DiscoveredTarget {
        name: found.name,
        command: found.server_config.command,
        args: found.server_config.args,
        env_names: found.server_config.env.into_keys().collect(),
    }))
}

/// Translate the CLI auth flags into a [`commands::UrlTarget`].
///
/// Returns `Ok(None)` for the stdio default. Returns `Err(...)` when the
/// chosen recipe needs a flag the operator did not supply (for example
/// `--auth bearer` without `--auth-env`).
pub(crate) fn build_url_target(args: &InitArgs) -> Result<Option<commands::UrlTarget>, String> {
    let Some(url) = args.url.clone() else {
        return Ok(None);
    };
    let auth = match args.auth {
        InitAuthKind::None => commands::InitAuth::None,
        InitAuthKind::Bearer => {
            let env_var = args
                .auth_env
                .clone()
                .ok_or_else(|| "--auth bearer requires --auth-env <NAME>".to_owned())?;
            commands::InitAuth::Bearer { env_var }
        }
        InitAuthKind::Oauth2 => commands::InitAuth::OAuth2,
        InitAuthKind::CustomHeader => {
            let name = args
                .header_name
                .clone()
                .ok_or_else(|| "--auth custom-header requires --header-name <NAME>".to_owned())?;
            commands::InitAuth::CustomHeader {
                name,
                env_var: args.header_env.clone(),
            }
        }
    };
    Ok(Some(commands::UrlTarget {
        url,
        auth,
        wait_for_ready: args.wait_for_ready,
    }))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn build_url_target_returns_none_without_url() {
        let args = InitArgs::default();
        let target = build_url_target(&args).expect("ok");
        assert!(target.is_none());
    }

    #[test]
    fn build_url_target_rejects_bearer_without_env() {
        let args = InitArgs {
            url: Some("https://mcp.example.com/v1".into()),
            auth: InitAuthKind::Bearer,
            ..InitArgs::default()
        };
        let err = build_url_target(&args).unwrap_err();
        assert!(err.contains("--auth-env"));
    }

    #[test]
    fn build_url_target_routes_oauth2() {
        let args = InitArgs {
            url: Some("https://mcp.example.com/v1".into()),
            auth: InitAuthKind::Oauth2,
            ..InitArgs::default()
        };
        let target = build_url_target(&args).expect("ok").expect("some");
        assert!(matches!(target.auth, commands::InitAuth::OAuth2));
    }

    #[test]
    fn build_url_target_rejects_custom_header_without_name() {
        let args = InitArgs {
            url: Some("https://mcp.example.com/v1".into()),
            auth: InitAuthKind::CustomHeader,
            ..InitArgs::default()
        };
        let err = build_url_target(&args).unwrap_err();
        assert!(err.contains("--header-name"));
    }
}
