//! Credential (vault item) access.

use crate::message::Context;
use crate::runtime::{client, InVariable, Result, RobomotionError};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

/// Credential configuration for vault access.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Credential {
    #[serde(default)]
    pub scope: String,

    #[serde(default)]
    pub name: Value,

    /// Deprecated: Use name instead
    #[serde(default, rename = "vaultId")]
    pub vault_id: String,

    /// Deprecated: Use name instead
    #[serde(default, rename = "itemId")]
    pub item_id: String,
}

#[derive(Debug, Clone, Deserialize)]
struct CredentialRef {
    #[serde(rename = "vaultId")]
    vault_id: String,
    #[serde(rename = "itemId")]
    item_id: String,
}

impl Credential {
    /// Get the credential (vault item) data.
    pub async fn get(&self, ctx: &Context) -> Result<HashMap<String, Value>> {
        // Use deprecated fields if available
        if !self.vault_id.is_empty() && !self.item_id.is_empty() {
            return client::get_vault_item(&self.vault_id, &self.item_id).await;
        }

        // Get credential reference from name
        let creds = self.get_credential_ref(ctx).await?;
        client::get_vault_item(&creds.vault_id, &creds.item_id).await
    }

    /// Set the credential (vault item) data.
    pub async fn set(&self, ctx: &Context, data: &[u8]) -> Result<HashMap<String, Value>> {
        // Use deprecated fields if available
        if !self.vault_id.is_empty() && !self.item_id.is_empty() {
            return client::set_vault_item(&self.vault_id, &self.item_id, data).await;
        }

        // Get credential reference from name
        let creds = self.get_credential_ref(ctx).await?;
        client::set_vault_item(&creds.vault_id, &creds.item_id, data).await
    }

    async fn get_credential_ref(&self, ctx: &Context) -> Result<CredentialRef> {
        let value = if self.scope == "Message" {
            let name = self
                .name
                .as_str()
                .ok_or_else(|| RobomotionError::Variable("Invalid credential name".to_string()))?;

            let var: InVariable<Value> = InVariable::new(
                self.scope.clone(),
                Value::String(name.to_string()),
            );
            var.get(ctx).await?
        } else {
            self.name.clone()
        };

        serde_json::from_value(value).map_err(|e| {
            RobomotionError::Variable(format!("Invalid credential reference: {}", e))
        })
    }
}

