//! Credential storage (access/secret key metadata)

use std::sync::Arc;

use iam_types::{CredentialRecord, Result};

use crate::backend::{Backend, CasResult, JsonStore, StorageBackend};

/// Store for credentials (S3/API keys)
pub struct CredentialStore {
    backend: Arc<Backend>,
}

impl JsonStore for CredentialStore {
    fn backend(&self) -> &Backend {
        &self.backend
    }
}

impl CredentialStore {
    pub fn new(backend: Arc<Backend>) -> Self {
        Self { backend }
    }

    pub async fn put(&self, record: &CredentialRecord) -> Result<u64> {
        let key = CredentialRecord::storage_key(&record.access_key_id);
        self.put_json(key.as_bytes(), record).await
    }

    pub async fn get(&self, access_key_id: &str) -> Result<Option<(CredentialRecord, u64)>> {
        let key = CredentialRecord::storage_key(access_key_id);
        self.get_json(key.as_bytes()).await
    }

    pub async fn list_for_principal(
        &self,
        principal_id: &str,
        limit: u32,
    ) -> Result<Vec<CredentialRecord>> {
        let prefix = b"iam/credentials/";
        let items = self.backend.scan_prefix(prefix, limit).await?;
        let mut credentials = Vec::new();
        for pair in items {
            let record: CredentialRecord = serde_json::from_slice(&pair.value)
                .map_err(|e| iam_types::Error::Serialization(e.to_string()))?;
            if record.principal_id == principal_id {
                credentials.push(record);
            }
        }
        Ok(credentials)
    }

    pub async fn revoke(&self, access_key_id: &str) -> Result<bool> {
        let key = CredentialRecord::storage_key(access_key_id);
        let current = self.get_json::<CredentialRecord>(key.as_bytes()).await?;
        let (mut record, version) = match current {
            Some(v) => v,
            None => return Ok(false),
        };
        if record.revoked {
            return Ok(false);
        }
        record.revoked = true;
        match self.cas_json(key.as_bytes(), version, &record).await? {
            CasResult::Success(_) => Ok(true),
            CasResult::Conflict { .. } => Ok(false),
            CasResult::NotFound => Ok(false),
        }
    }
}
