//! Admission control storage for graduated PoW and trust tiers.
//!
//! The AdmissionStore provides spam protection for Episteme by requiring new or
//! untrusted agents to solve proof-of-work puzzles before their assertions are accepted.
//! As agents demonstrate good behavior (accurate assertions, verified against gold standards),
//! they graduate to higher trust tiers with reduced PoW requirements and increased quotas.
//!
//! # Key Design Decision: Reuse TrustRank Data
//!
//! The AdmissionStore wraps `TrustRankStore` rather than creating new storage keys.
//! The existing `TrustRank` struct already has:
//! - `score: f32` -> maps to TrustTier for quota multipliers
//! - `assertions_count: u64` -> used for PoW graduation thresholds
//!
//! This means no schema migration is needed and admission control is automatically
//! integrated with the existing reputation system.
//!
//! # Graduated PoW Difficulty
//!
//! | Assertions | Trust Score | Difficulty | Effort |
//! |------------|-------------|------------|--------|
//! | 0-9        | < 0.6       | 16 bits    | ~16 sec |
//! | 10-49      | < 0.6       | 1 bit      | trivial |
//! | 50+        | any         | 0 bits     | exempt |
//! | any        | >= 0.6      | 0 bits     | exempt |

mod model;
mod store_impl;

pub use model::{AdmissionStatus, AdmissionStatusResult};
pub use store_impl::GenericAdmissionStore;

use crate::error::Result;
use async_trait::async_trait;
use stemedb_core::types::{AdmissionConfig, PowError, PowProof};

/// Specialized storage trait for admission control operations.
///
/// This trait provides PoW-based spam protection layered on top of the existing
/// TrustRankStore. It computes admission status, verifies proofs, and records
/// assertion activity for graduation tracking.
///
/// # Example
///
/// ```ignore
/// let admission_store = GenericAdmissionStore::new(trust_rank_store, config);
///
/// // Get admission status for an agent
/// let status = admission_store.get_admission_status(&agent_id).await?;
///
/// if status.pow_required {
///     // Agent must submit a valid PoW proof
///     admission_store.verify_pow(&proof, server_time).await?;
/// }
///
/// // After successful assertion, record it
/// admission_store.record_assertion(&agent_id, timestamp).await?;
/// ```
#[async_trait]
pub trait AdmissionStore: Send + Sync {
    /// Get the current admission status for an agent.
    ///
    /// This returns the agent's trust tier, PoW requirements, and quota multipliers
    /// based on their trust score and assertion count.
    ///
    /// # Arguments
    /// * `agent_id` - The agent's Ed25519 public key
    ///
    /// # Returns
    /// The agent's current admission status.
    async fn get_admission_status(&self, agent_id: &[u8; 32]) -> Result<AdmissionStatus>;

    /// Verify a proof-of-work solution.
    ///
    /// This validates that:
    /// 1. The proof timestamp is within the allowed window
    /// 2. The hash has sufficient leading zeros for the required difficulty
    ///
    /// Note: The caller should verify the agent_id in the proof matches the request.
    ///
    /// # Arguments
    /// * `proof` - The PoW solution submitted by the agent
    /// * `required_difficulty` - Number of leading zero bits required
    /// * `server_time` - Current server timestamp for validation
    ///
    /// # Returns
    /// `Ok(())` if the proof is valid, or `PowError` describing the failure.
    async fn verify_pow(
        &self,
        proof: &PowProof,
        required_difficulty: u8,
        server_time: u64,
    ) -> Result<std::result::Result<(), PowError>>;

    /// Compute the required PoW difficulty for an agent.
    ///
    /// This considers both assertion count and trust score:
    /// - First 10 assertions with trust < 0.6: 16 bits
    /// - Assertions 10-49 with trust < 0.6: 1 bit
    /// - 50+ assertions OR trust >= 0.6: 0 bits (exempt)
    ///
    /// # Arguments
    /// * `agent_id` - The agent's Ed25519 public key
    ///
    /// # Returns
    /// Required difficulty in bits (0 = exempt).
    async fn compute_difficulty(&self, agent_id: &[u8; 32]) -> Result<u8>;

    /// Record a successful assertion for graduation tracking.
    ///
    /// Increments the agent's assertion count in TrustRank. This is called
    /// after an assertion passes validation and is stored.
    ///
    /// # Arguments
    /// * `agent_id` - The agent's Ed25519 public key
    /// * `timestamp` - Unix timestamp of the assertion
    ///
    /// # Returns
    /// The new assertion count.
    async fn record_assertion(&self, agent_id: &[u8; 32], timestamp: u64) -> Result<u64>;

    /// Get the admission configuration.
    fn config(&self) -> &AdmissionConfig;
}

/// Extension trait for checking admission in a single call.
///
/// This is a convenience trait that combines status check and PoW verification.
#[async_trait]
pub trait AdmissionCheck: AdmissionStore {
    /// Check if a request should be admitted, optionally with a PoW proof.
    ///
    /// This is the primary entry point for the admission middleware:
    /// 1. Get admission status
    /// 2. If PoW required and proof provided, verify it
    /// 3. If PoW required and no proof, return POW_REQUIRED status
    /// 4. Return success if admitted
    ///
    /// # Arguments
    /// * `agent_id` - The agent's Ed25519 public key
    /// * `proof` - Optional PoW proof (from X-PoW-Nonce/X-PoW-Timestamp headers)
    /// * `server_time` - Current server timestamp
    ///
    /// # Returns
    /// The result of the admission check with full status details.
    async fn check_admission(
        &self,
        agent_id: &[u8; 32],
        proof: Option<&PowProof>,
        server_time: u64,
    ) -> Result<AdmissionStatusResult>;
}

// Blanket implementation of AdmissionCheck for all AdmissionStore implementors
#[async_trait]
impl<T: AdmissionStore + Sync> AdmissionCheck for T {
    async fn check_admission(
        &self,
        agent_id: &[u8; 32],
        proof: Option<&PowProof>,
        server_time: u64,
    ) -> Result<AdmissionStatusResult> {
        let status = self.get_admission_status(agent_id).await?;

        if !status.pow_required {
            // No PoW needed, admit immediately
            return Ok(AdmissionStatusResult::Admitted(status));
        }

        // PoW is required
        match proof {
            Some(p) => {
                // Verify agent_id matches
                if p.agent_id != *agent_id {
                    return Ok(AdmissionStatusResult::PowFailed {
                        status,
                        error: PowError::AgentIdMismatch,
                    });
                }

                // Verify the proof
                match self.verify_pow(p, status.pow_difficulty, server_time).await? {
                    Ok(()) => Ok(AdmissionStatusResult::Admitted(status)),
                    Err(e) => Ok(AdmissionStatusResult::PowFailed { status, error: e }),
                }
            }
            None => {
                // No proof provided but required
                Ok(AdmissionStatusResult::PowRequired(status))
            }
        }
    }
}
