//! Per-agent circuit breaker storage for misbehavior isolation.
//!
//! Circuit breakers temporarily ban agents that repeatedly misbehave
//! (invalid signatures, malformed input, PoW failures, quota violations).
//!
//! # State Machine
//!
//! ```text
//!          ┌─────────────────────────────────────────┐
//!          │                                         │
//!          ▼                                         │
//!     ┌─────────┐    5 failures    ┌─────────┐      │
//!     │ CLOSED  │ ───────────────► │  OPEN   │      │
//!     │ (normal)│                  │ (banned)│      │
//!     └─────────┘                  └────┬────┘      │
//!          ▲                            │           │
//!          │                     30 sec timeout     │
//!          │                            │           │
//!          │                            ▼           │
//!          │   1 success         ┌───────────┐     │ 1 failure
//!          └─────────────────────│ HALF_OPEN │─────┘
//!                                │ (testing) │
//!                                └───────────┘
//! ```
//!
//! # Usage
//!
//! ```ignore
//! use stemedb_storage::{HybridStore, GenericCircuitBreakerStore, CircuitBreakerStore};
//!
//! let kv_store = HybridStore::open("./data")?;
//! let cb_store = GenericCircuitBreakerStore::new(kv_store, CircuitBreakerConfig::default());
//!
//! // Check if agent is allowed
//! if cb_store.check_allowed(&agent_id).await? {
//!     // Process request...
//!
//!     // On failure:
//!     cb_store.record_failure(&agent_id, FailureType::InvalidSignature).await?;
//!
//!     // On success:
//!     cb_store.record_success(&agent_id).await?;
//! } else {
//!     // Reject request, circuit is open
//! }
//! ```

mod model;
mod store_impl;

pub use model::{CircuitBreakerConfig, CircuitBreakerRecord, CircuitState, FailureType};
pub use store_impl::GenericCircuitBreakerStore;

use crate::Result;
use async_trait::async_trait;

/// Storage trait for per-agent circuit breakers.
///
/// Provides operations for tracking failures, managing circuit state,
/// and checking whether agents are allowed to make requests.
#[async_trait]
pub trait CircuitBreakerStore: Send + Sync {
    /// Get the current circuit breaker record for an agent.
    ///
    /// Returns `None` if no record exists (agent is in good standing).
    async fn get_circuit(&self, agent_id: &[u8; 32]) -> Result<Option<CircuitBreakerRecord>>;

    /// Record a failure for an agent.
    ///
    /// Increments the failure count and potentially trips the circuit.
    /// Returns the updated circuit record.
    async fn record_failure(
        &self,
        agent_id: &[u8; 32],
        failure_type: FailureType,
        timestamp: u64,
    ) -> Result<CircuitBreakerRecord>;

    /// Record a success for an agent.
    ///
    /// If the circuit is HalfOpen, this closes it (resets to normal).
    /// If the circuit is Closed, this is a no-op.
    async fn record_success(&self, agent_id: &[u8; 32], timestamp: u64) -> Result<()>;

    /// Reset a circuit breaker manually (admin operation).
    ///
    /// Returns `Ok(())` even if no circuit exists.
    async fn reset_circuit(&self, agent_id: &[u8; 32]) -> Result<()>;

    /// List all tripped (Open or HalfOpen) circuit breakers.
    ///
    /// Returns records ordered by last failure timestamp.
    async fn list_tripped(&self, limit: usize) -> Result<Vec<CircuitBreakerRecord>>;

    /// Check if an agent is allowed to make requests.
    ///
    /// Returns `true` if circuit is Closed or HalfOpen (testing).
    /// Returns `false` if circuit is Open (banned).
    ///
    /// This method also transitions Open circuits to HalfOpen
    /// if the timeout has elapsed.
    async fn check_allowed(&self, agent_id: &[u8; 32], current_time: u64) -> Result<bool>;

    /// Get the number of seconds until an agent can retry.
    ///
    /// Returns `None` if the agent is not blocked.
    /// Returns `Some(0)` if the timeout has elapsed.
    async fn retry_after(&self, agent_id: &[u8; 32], current_time: u64) -> Result<Option<u64>>;
}
