//! Data models for quota tracking.

/// Cost configuration for operations.
#[derive(Debug, Clone, Copy)]
pub struct CostConfig {
    /// Base cost for creating an assertion (default: 10).
    pub assert_cost: u64,
    /// Base cost for casting a vote (default: 1).
    pub vote_cost: u64,
    /// Base cost for a query (default: 5).
    pub query_base_cost: u64,
    /// Additional cost per lens applied to a query (default: 1).
    pub query_lens_cost: u64,
    /// Cost per kilobyte of payload (default: 1).
    pub payload_kb_cost: u64,
}

impl Default for CostConfig {
    fn default() -> Self {
        Self {
            assert_cost: 10,
            vote_cost: 1,
            query_base_cost: 5,
            query_lens_cost: 1,
            payload_kb_cost: 1,
        }
    }
}

impl CostConfig {
    /// Calculate the cost for an assertion operation.
    ///
    /// # Arguments
    /// * `payload_bytes` - Size of the assertion payload in bytes
    pub fn cost_for_assert(&self, payload_bytes: usize) -> u64 {
        let kb = (payload_bytes as u64).saturating_add(1023) / 1024; // Round up
        self.assert_cost.saturating_add(kb.saturating_mul(self.payload_kb_cost))
    }

    /// Calculate the cost for a vote operation.
    ///
    /// # Arguments
    /// * `payload_bytes` - Size of the vote payload in bytes
    pub fn cost_for_vote(&self, payload_bytes: usize) -> u64 {
        let kb = (payload_bytes as u64).saturating_add(1023) / 1024;
        self.vote_cost.saturating_add(kb.saturating_mul(self.payload_kb_cost))
    }

    /// Calculate the cost for a query operation.
    ///
    /// # Arguments
    /// * `lens_count` - Number of lenses applied
    /// * `payload_bytes` - Size of the query params in bytes (usually small)
    pub fn cost_for_query(&self, lens_count: u64, payload_bytes: usize) -> u64 {
        let kb = (payload_bytes as u64).saturating_add(1023) / 1024;
        self.query_base_cost
            .saturating_add(lens_count.saturating_mul(self.query_lens_cost))
            .saturating_add(kb.saturating_mul(self.payload_kb_cost))
    }
}

/// Operation type for cost calculation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationType {
    /// Creating a new assertion.
    Assert,
    /// Casting a vote.
    Vote,
    /// Querying assertions.
    Query {
        /// Number of lenses applied.
        lens_count: u64,
    },
}

/// Quota record for a single agent in a single hour window.
#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Clone, PartialEq)]
#[archive(check_bytes)]
pub struct QuotaRecord {
    /// The agent's Ed25519 public key.
    pub agent_id: [u8; 32],
    /// Unix timestamp of the hour window start (truncated to hour).
    pub window_start: u64,
    /// Total tokens consumed in this window.
    pub tokens_used: u64,
    /// Number of operations in this window.
    pub operation_count: u64,
    /// Last operation timestamp.
    pub last_operation: u64,
}

impl QuotaRecord {
    /// Create a new quota record for an agent and hour window.
    pub fn new(agent_id: [u8; 32], window_start: u64) -> Self {
        Self { agent_id, window_start, tokens_used: 0, operation_count: 0, last_operation: 0 }
    }

    /// Record a cost against this quota.
    ///
    /// # Arguments
    /// * `cost` - Number of tokens to consume
    /// * `timestamp` - Current timestamp
    pub fn record(&mut self, cost: u64, timestamp: u64) {
        self.tokens_used = self.tokens_used.saturating_add(cost);
        self.operation_count = self.operation_count.saturating_add(1);
        self.last_operation = timestamp;
    }

    /// Check if the quota would be exceeded by a given cost.
    ///
    /// # Arguments
    /// * `cost` - Proposed cost to check
    /// * `limit` - Quota limit for this window
    ///
    /// # Returns
    /// `true` if the operation would exceed the quota
    pub fn would_exceed(&self, cost: u64, limit: u64) -> bool {
        self.tokens_used.saturating_add(cost) > limit
    }

    /// Calculate remaining tokens in the quota.
    ///
    /// # Arguments
    /// * `limit` - Quota limit for this window
    pub fn remaining(&self, limit: u64) -> u64 {
        limit.saturating_sub(self.tokens_used)
    }
}

/// Result of a quota check.
#[derive(Debug, Clone)]
pub struct QuotaCheckResult {
    /// Whether the operation is allowed.
    pub allowed: bool,
    /// Remaining tokens after this operation (if allowed).
    pub remaining: u64,
    /// Quota limit for this window.
    pub limit: u64,
    /// Unix timestamp when this quota window resets.
    pub reset_at: u64,
    /// Cost that was (or would be) charged.
    pub cost: u64,
}
