use serde::Deserialize;
use serde_json::json;

use super::{common, ToolResult};

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct InjectMapping {
    pub secret: String,
    pub env_var: String,
}

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct InjectParams {
    pub mappings: Vec<InjectMapping>,
    pub command: Vec<String>,
}

pub async fn execute(params: InjectParams) -> ToolResult {
    common::validate_command(&params.command)?;
    if params.mappings.len() > common::MAX_COMMAND_ARGS {
        return Err(format!(
            "too many mappings (max {})",
            common::MAX_COMMAND_ARGS
        ));
    }
    if params.mappings.is_empty() {
        return Err("at least one secret mapping is required".to_string());
    }
    for mapping in &params.mappings {
        common::validate_secret_name(&mapping.secret, "mappings[].secret")?;
        common::require_trimmed_non_empty(&mapping.env_var, "mappings[].env_var")?;
        common::validate_bounded_string(
            &mapping.env_var,
            "mappings[].env_var",
            common::MAX_STRING_LENGTH,
        )?;
    }

    tokio::task::spawn_blocking(move || -> Result<(), String> {
        let vault = envseal::vault::Vault::open_default().map_err(|e| e.to_string())?;
        let options = envseal::inject::InjectExecOptions {
            isolate_stdio: true,
        };
        if params.mappings.len() == 1 {
            let mapping = &params.mappings[0];
            let request = envseal::inject::InjectRequest {
                secret_name: &mapping.secret,
                env_var: &mapping.env_var,
                command: &params.command,
            };
            envseal::inject::execute_with_options(&vault, &request, options)
                .map_err(|e| e.to_string())?;
        } else {
            let refs = params
                .mappings
                .iter()
                .map(|m| (m.secret.as_str(), m.env_var.as_str()))
                .collect::<Vec<_>>();
            envseal::inject::execute_multi_with_options(&vault, &refs, &params.command, options)
                .map_err(|e| e.to_string())?;
        }
        Ok(())
    })
    .await
    .map_err(|e| format!("inject task failed: {e}"))??;

    Ok(json!({
        "status": "completed",
        "mode": "inject",
        "exit_code": 0
    }))
}
