//! `fafb-q config` command — validate, dump, and show example server configs.

use std::path::Path;

use colored::Colorize;

use crate::cli::{
    CliConfig, CliError, ConfigAction, OutputFormat, build_http_client, extract_error_message,
    http_base_url, response_json,
};
use crate::config::ServerConfig;

/// Embedded example config file — compiled in from `config/config.example.toml`.
const EXAMPLE_CONFIG: &str = include_str!("../../config/config.example.toml");

pub async fn run(config: CliConfig, action: ConfigAction) -> Result<(), CliError> {
    match action {
        ConfigAction::Validate { file } => run_validate(&file).await,
        ConfigAction::Dump { file, admin_key } => {
            run_dump(config, file.as_deref(), admin_key.as_deref()).await
        }
        ConfigAction::Example => run_example().await,
    }
}

async fn run_validate(file: &str) -> Result<(), CliError> {
    let path = Path::new(file);
    if !path.exists() {
        return Err(CliError::Error(format!("config file not found: {file}")));
    }

    match ServerConfig::load(path) {
        Ok(_) => {
            println!("{} {file}", "valid:".green());
            Ok(())
        }
        Err(e) => {
            eprintln!("{} {file}", "invalid:".red());
            // Print each error on its own line for readability.
            for part in e.to_string().split("; ") {
                eprintln!("  {} {part}", "-".red());
            }
            Err(CliError::Error("config validation failed".into()))
        }
    }
}

async fn run_dump(
    config: CliConfig,
    file: Option<&str>,
    admin_key: Option<&str>,
) -> Result<(), CliError> {
    let format = OutputFormat::auto_detect(config.json, false);

    // Remote dump: fetch from running server via GET /admin/config.
    if let Some(key) = admin_key {
        return run_dump_remote(&config, key, format).await;
    }

    // Local dump: load from file or search standard paths.
    let cfg = if let Some(f) = file {
        let path = Path::new(f);
        if !path.exists() {
            return Err(CliError::Error(format!("config file not found: {f}")));
        }
        ServerConfig::load(path)
            .map_err(|e| CliError::Error(format!("failed to load config: {e}")))?
    } else {
        let found = crate::config::find_config_path(None, |key| std::env::var(key).ok());
        match found {
            Some(path) => ServerConfig::load(&path)
                .map_err(|e| CliError::Error(format!("failed to load config: {e}")))?,
            None => {
                return Err(CliError::Error(
                    "no config file found — use 'config dump --file <path>' or '--admin-key' to fetch from a running server".into(),
                ));
            }
        }
    };

    match format {
        OutputFormat::Json => {
            let toml_str = cfg.dump_redacted();
            let val: toml::Value = toml::from_str(&toml_str)
                .map_err(|e| CliError::Error(format!("failed to serialize config: {e}")))?;
            let json = serde_json::to_string_pretty(&val)
                .map_err(|e| CliError::Error(format!("failed to serialize config: {e}")))?;
            println!("{json}");
        }
        _ => {
            let toml_str = cfg.dump_redacted();
            print!("{toml_str}");
        }
    }

    Ok(())
}

/// Fetch the running server's config via `GET /admin/config`.
async fn run_dump_remote(
    config: &CliConfig,
    admin_key: &str,
    format: OutputFormat,
) -> Result<(), CliError> {
    let client = build_http_client(config)?;
    let url = format!("{}/admin/config", http_base_url(config));

    let resp = client
        .get(&url)
        .bearer_auth(admin_key)
        .send()
        .await
        .map_err(|e| {
            if e.is_timeout() {
                CliError::Timeout(format!("request timed out: {e}"))
            } else if e.is_connect() {
                CliError::Error(format!("connection failed: {e}"))
            } else {
                CliError::Error(format!("request failed: {e}"))
            }
        })?;

    let status = resp.status();
    let body = response_json(resp).await?;

    if !status.is_success() {
        let msg = extract_error_message(&body);
        return Err(CliError::Error(format!("server returned {status}: {msg}")));
    }

    match format {
        OutputFormat::Json => {
            let pretty = serde_json::to_string_pretty(&body)
                .map_err(|e| CliError::Error(format!("failed to format JSON: {e}")))?;
            println!("{pretty}");
        }
        _ => {
            let pretty = serde_json::to_string_pretty(&body)
                .map_err(|e| CliError::Error(format!("failed to format JSON: {e}")))?;
            println!("{pretty}");
        }
    }

    Ok(())
}

async fn run_example() -> Result<(), CliError> {
    print!("{EXAMPLE_CONFIG}");
    Ok(())
}
