//! In-memory `EpochRevocation` adapter for engine integration tests.

use std::collections::HashMap;
use std::sync::Mutex;

use ppoppo_token::{EpochRevocation, EpochRevocationError};

#[derive(Debug)]
pub struct MemoryEpochRevocation {
    versions: Mutex<HashMap<String, i64>>,
    failing: bool,
}

impl MemoryEpochRevocation {
    /// Working substrate — subjects with no recorded version return 0
    /// (genesis convention, matching the chat-auth fetcher contract).
    pub fn new() -> Self {
        Self {
            versions: Mutex::new(HashMap::new()),
            failing: false,
        }
    }

    /// Always-failing substrate — every call returns `Transient`.
    pub fn failing() -> Self {
        Self {
            versions: Mutex::new(HashMap::new()),
            failing: true,
        }
    }

    /// Set the current epoch for `sub`. Mirrors a break-glass /
    /// `LogoutAll` write to `sv:{ppnum_id}` (KVRocks) — see
    /// STANDARDS_AUTH_INVALIDATION §2.3 for the write-side primitive.
    pub fn bump(&self, sub: &str, to: i64) {
        let mut v = self.versions.lock().unwrap_or_else(|p| p.into_inner());
        v.insert(sub.to_string(), to);
    }
}

impl Default for MemoryEpochRevocation {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait::async_trait]
impl EpochRevocation for MemoryEpochRevocation {
    async fn current(&self, sub: &str) -> Result<i64, EpochRevocationError> {
        if self.failing {
            return Err(EpochRevocationError::Transient(
                "MemoryEpochRevocation::failing()".to_string(),
            ));
        }
        let v = self.versions.lock().unwrap_or_else(|p| p.into_inner());
        Ok(v.get(sub).copied().unwrap_or(0))
    }
}
