//! TrustRank data model and core reputation logic.
//!
//! This module defines the TrustRank struct which tracks per-agent reputation
//! scores, accuracy metrics, and confidence decay mechanics.

/// Default TrustRank score for new agents.
pub const DEFAULT_TRUST_SCORE: f32 = 0.5;

/// Default half-life for confidence decay (30 days in seconds).
pub const DEFAULT_HALF_LIFE_SECONDS: u64 = 30 * 24 * 60 * 60;

/// TrustRank reputation data for an agent.
///
/// This struct tracks the reputation score and learning metrics for a single agent.
/// All fields are updated atomically when reputation changes.
///
/// # Invariants
///
/// - `score` is always in range [0.0, 1.0]
/// - `last_updated` is Unix timestamp (seconds since epoch)
/// - `assertions_count >= accuracy_count` (can't be accurate more than total)
#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Clone, PartialEq)]
#[archive(check_bytes)]
pub struct TrustRank {
    /// The agent's Ed25519 public key.
    pub agent_id: [u8; 32],
    /// Reputation score (0.0 to 1.0).
    pub score: f32,
    /// Unix timestamp of last update (seconds since epoch).
    pub last_updated: u64,
    /// Total number of assertions made by this agent.
    pub assertions_count: u64,
    /// Number of assertions that were deemed accurate.
    pub accuracy_count: u64,
}

impl TrustRank {
    /// Create a new TrustRank for an agent with default initial score.
    pub fn new(agent_id: [u8; 32], timestamp: u64) -> Self {
        Self {
            agent_id,
            score: DEFAULT_TRUST_SCORE,
            last_updated: timestamp,
            assertions_count: 0,
            accuracy_count: 0,
        }
    }

    /// Apply confidence half-life decay to the score.
    ///
    /// Formula: `score = score * (0.5 ^ (elapsed_days / half_life_days))`
    ///
    /// # Arguments
    /// * `current_timestamp` - Current Unix timestamp in seconds
    /// * `half_life_seconds` - Half-life duration in seconds
    ///
    /// # Returns
    /// The decayed score, clamped to [0.0, 1.0]
    pub fn apply_decay(&mut self, current_timestamp: u64, half_life_seconds: u64) -> f32 {
        if current_timestamp <= self.last_updated {
            return self.score;
        }

        let elapsed_seconds = current_timestamp - self.last_updated;
        let elapsed_days = elapsed_seconds as f64 / (24.0 * 60.0 * 60.0);
        let half_life_days = half_life_seconds as f64 / (24.0 * 60.0 * 60.0);

        let decay_factor = 0.5_f64.powf(elapsed_days / half_life_days);
        self.score = (self.score as f64 * decay_factor).clamp(0.0, 1.0) as f32;
        self.last_updated = current_timestamp;

        self.score
    }

    /// Update the score by a delta, clamping to [0.0, 1.0].
    ///
    /// # Arguments
    /// * `delta` - Amount to add to the score (can be negative)
    /// * `timestamp` - Timestamp of this update
    ///
    /// # Returns
    /// The new score after update and clamping
    pub fn adjust_score(&mut self, delta: f32, timestamp: u64) -> f32 {
        self.score = (self.score + delta).clamp(0.0, 1.0);
        self.last_updated = timestamp;
        self.score
    }

    /// Record an assertion outcome for learning loop tracking.
    ///
    /// # Arguments
    /// * `was_accurate` - Whether the assertion was correct
    pub fn record_outcome(&mut self, was_accurate: bool) {
        self.assertions_count = self.assertions_count.saturating_add(1);
        if was_accurate {
            self.accuracy_count = self.accuracy_count.saturating_add(1);
        }
    }

    /// Calculate the agent's accuracy rate.
    ///
    /// # Returns
    /// Accuracy rate in [0.0, 1.0], or 0.0 if no assertions have been made
    pub fn accuracy_rate(&self) -> f32 {
        if self.assertions_count == 0 {
            return 0.0;
        }
        self.accuracy_count as f32 / self.assertions_count as f32
    }
}

/// Result of verifying an agent's answer against a gold standard.
///
/// Tracks whether the agent was rewarded or penalized, and by how much.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrustAdjustment {
    /// Agent's answer matched the gold standard.
    ///
    /// The f32 value is the amount of trust added.
    Rewarded(f32),

    /// Agent's answer did NOT match the gold standard.
    ///
    /// The f32 value is the amount of trust removed (absolute value).
    Penalized(f32),

    /// Agent has already verified this gold standard.
    ///
    /// No trust adjustment is made. This prevents agents from repeatedly
    /// verifying the same gold standard to farm TrustRank.
    AlreadyVerified,
}

impl TrustAdjustment {
    /// Get the delta to apply to the trust score.
    ///
    /// Rewards are positive, penalties are negative, already verified is zero.
    pub fn delta(&self) -> f32 {
        match self {
            TrustAdjustment::Rewarded(amount) => *amount,
            TrustAdjustment::Penalized(amount) => -amount,
            TrustAdjustment::AlreadyVerified => 0.0,
        }
    }

    /// Check if this was a reward.
    pub fn is_reward(&self) -> bool {
        matches!(self, TrustAdjustment::Rewarded(_))
    }

    /// Check if this was a penalty.
    pub fn is_penalty(&self) -> bool {
        matches!(self, TrustAdjustment::Penalized(_))
    }

    /// Check if this was already verified.
    pub fn is_already_verified(&self) -> bool {
        matches!(self, TrustAdjustment::AlreadyVerified)
    }
}
