//! Vault domain tools — health/seal status, key operations.
//!
//! These tools expose the KMS backend (Age/Cosmian/RustyVault/SecretumVault) through
//! the common Tool interface. "Seal/unseal" maps to health-check semantics — the
//! underlying backends use different auth models, so unseal is best-effort.

use crate::protocol::{ToolCategory, ToolError};
use crate::sources::VaultSource;
use crate::tool::{Context, Tool};
use async_trait::async_trait;
use serde_json::{Value, json};
use std::sync::Arc;

// ── vault_seal_status ─────────────────────────────────────────────────────────

pub struct VaultSealStatus {
    vault: Arc<VaultSource>,
}
impl VaultSealStatus {
    pub fn new(vault: Arc<VaultSource>) -> Self { Self { vault } }
}

#[async_trait]
impl Tool for VaultSealStatus {
    fn name(&self) -> &'static str { "vault_seal_status" }
    fn description(&self) -> &'static str {
        "Check if the KMS vault backend is reachable (sealed = unreachable)"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, _params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        match self.vault.health().await {
            Ok(healthy) => Ok(json!({
                "sealed":  !healthy,
                "healthy": healthy,
                "backend": self.vault.backend_name(),
            })),
            Err(_) => Ok(json!({
                "sealed":  true,
                "healthy": false,
                "backend": self.vault.backend_name(),
            })),
        }
    }
}

// ── vault_list ────────────────────────────────────────────────────────────────

pub struct VaultList {
    vault: Arc<VaultSource>,
}
impl VaultList {
    pub fn new(vault: Arc<VaultSource>) -> Self { Self { vault } }
}

#[async_trait]
impl Tool for VaultList {
    fn name(&self) -> &'static str { "vault_list" }
    fn description(&self) -> &'static str {
        "List vault backend info and available KMS operations"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, _params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        let healthy = self.vault.health().await.unwrap_or(false);
        Ok(json!({
            "backend": self.vault.backend_name(),
            "healthy": healthy,
            "operations": ["encrypt", "decrypt", "generate_data_key"],
        }))
    }
}

// ── vault_get ─────────────────────────────────────────────────────────────────

pub struct VaultGet {
    vault: Arc<VaultSource>,
}
impl VaultGet {
    pub fn new(vault: Arc<VaultSource>) -> Self { Self { vault } }
}

#[async_trait]
impl Tool for VaultGet {
    fn name(&self) -> &'static str { "vault_get" }
    fn description(&self) -> &'static str {
        "Decrypt a ciphertext using the KMS backend (hex-encoded input and output)"
    }
    fn schema(&self) -> Value {
        json!({
            "type": "object",
            "required": ["ciphertext"],
            "properties": {
                "ciphertext": {
                    "type": "string",
                    "description": "Hex-encoded ciphertext to decrypt"
                },
                "namespace": {
                    "type": "string",
                    "description": "Encryption namespace / context label",
                    "default": "default"
                }
            }
        })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        use vault_service::EncryptionContext;

        let hex = params["ciphertext"]
            .as_str()
            .ok_or_else(|| ToolError::invalid_param("ciphertext", "required string"))?;
        let namespace = params["namespace"].as_str().unwrap_or("default");

        let ciphertext = hex::decode(hex).map_err(|e| {
            ToolError::invalid_param("ciphertext", format!("invalid hex: {e}"))
        })?;

        let mut ctx = EncryptionContext::new();
        ctx.add("namespace", namespace);

        let plaintext = self.vault.decrypt(&ciphertext, &ctx).await?;
        let result_hex = hex::encode(&plaintext);

        Ok(json!({
            "namespace":  namespace,
            "plaintext":  result_hex,
            "backend":    self.vault.backend_name(),
        }))
    }
}

// ── vault_unseal ──────────────────────────────────────────────────────────────

pub struct VaultUnseal {
    vault: Arc<VaultSource>,
}
impl VaultUnseal {
    pub fn new(vault: Arc<VaultSource>) -> Self { Self { vault } }
}

#[async_trait]
impl Tool for VaultUnseal {
    fn name(&self) -> &'static str { "vault_unseal" }
    fn description(&self) -> &'static str {
        "Attempt to unseal the vault backend — verifies connectivity and generates a probe data key"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Destructive }

    async fn invoke(&self, _params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        use vault_service::KeySpec;

        let backend = self.vault.backend_name();

        let spec = KeySpec::Aes256;
        match self.vault.generate_data_key(&spec).await {
            Ok(_) => Ok(json!({
                "unsealed": true,
                "backend":  backend,
            })),
            Err(e) => {
                // Connectivity failure — backend may truly be sealed / unreachable
                Err(ToolError::invocation(format!("unseal probe failed: {e}")))
            }
        }
    }
}
