//! Data Transfer Objects for admission control endpoints.

use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

/// Trust tier names for API responses.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub enum TrustTierDto {
    /// Untrusted tier: 0.0-0.3 trust score.
    Untrusted,
    /// Limited tier: 0.3-0.5 trust score.
    Limited,
    /// Verified tier: 0.5-0.7 trust score.
    Verified,
    /// Trusted tier: 0.7-0.9 trust score.
    Trusted,
    /// Authority tier: 0.9-1.0 trust score.
    Authority,
}

impl From<stemedb_core::types::TrustTier> for TrustTierDto {
    fn from(tier: stemedb_core::types::TrustTier) -> Self {
        match tier {
            stemedb_core::types::TrustTier::Untrusted => TrustTierDto::Untrusted,
            stemedb_core::types::TrustTier::Limited => TrustTierDto::Limited,
            stemedb_core::types::TrustTier::Verified => TrustTierDto::Verified,
            stemedb_core::types::TrustTier::Trusted => TrustTierDto::Trusted,
            stemedb_core::types::TrustTier::Authority => TrustTierDto::Authority,
        }
    }
}

/// Response for GET /v1/admission/status.
///
/// Contains the agent's current admission status including trust tier,
/// quota multipliers, and PoW requirements.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AdmissionStatusResponse {
    /// Agent's Ed25519 public key (hex-encoded).
    pub agent_id: String,

    /// Agent's trust tier.
    pub tier: TrustTierDto,

    /// Agent's current trust score (0.0 to 1.0).
    pub trust_score: f32,

    /// Total number of assertions made by this agent.
    pub assertions_count: u64,

    /// Required PoW difficulty in bits (0 = exempt).
    pub pow_difficulty: u8,

    /// Whether PoW is required for this agent's next submission.
    pub pow_required: bool,

    /// Base quota limit per hour (before tier multiplier).
    pub base_quota_limit: u64,

    /// Effective quota limit per hour (after tier multiplier).
    pub effective_quota_limit: u64,

    /// Quota multiplier for this tier.
    pub quota_multiplier: f32,

    /// Number of assertions until reduced PoW difficulty (or null if not applicable).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub assertions_until_reduced_difficulty: Option<u64>,

    /// Number of assertions until PoW exemption (or null if already exempt).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub assertions_until_exemption: Option<u64>,
}

impl AdmissionStatusResponse {
    /// Create a response from admission status.
    pub fn from_status(
        agent_id: String,
        status: &stemedb_storage::AdmissionStatus,
        config: &stemedb_core::types::AdmissionConfig,
    ) -> Self {
        // Calculate assertions until milestones
        let assertions_until_reduced_difficulty =
            if status.assertions_count < config.initial_threshold {
                Some(config.initial_threshold - status.assertions_count)
            } else {
                None
            };

        let assertions_until_exemption = if status.assertions_count < config.graduated_threshold
            && status.trust_score < config.trust_exemption_score
        {
            Some(config.graduated_threshold - status.assertions_count)
        } else {
            None
        };

        Self {
            agent_id,
            tier: status.tier.into(),
            trust_score: status.trust_score,
            assertions_count: status.assertions_count,
            pow_difficulty: status.pow_difficulty,
            pow_required: status.pow_required,
            base_quota_limit: status.base_quota_limit,
            effective_quota_limit: status.effective_quota_limit,
            quota_multiplier: status.quota_multiplier,
            assertions_until_reduced_difficulty,
            assertions_until_exemption,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use stemedb_core::types::{AdmissionConfig, TrustTier};
    use stemedb_storage::AdmissionStatus;

    #[test]
    fn test_tier_dto_conversion() {
        assert_eq!(TrustTierDto::from(TrustTier::Untrusted), TrustTierDto::Untrusted);
        assert_eq!(TrustTierDto::from(TrustTier::Limited), TrustTierDto::Limited);
        assert_eq!(TrustTierDto::from(TrustTier::Verified), TrustTierDto::Verified);
        assert_eq!(TrustTierDto::from(TrustTier::Trusted), TrustTierDto::Trusted);
        assert_eq!(TrustTierDto::from(TrustTier::Authority), TrustTierDto::Authority);
    }

    #[test]
    fn test_response_from_status_new_agent() {
        let status = AdmissionStatus::new(0.5, 0, 16);
        let config = AdmissionConfig::default();

        let response = AdmissionStatusResponse::from_status("abc123".to_string(), &status, &config);

        assert_eq!(response.tier, TrustTierDto::Verified);
        assert_eq!(response.pow_difficulty, 16);
        assert!(response.pow_required);
        assert_eq!(response.assertions_until_reduced_difficulty, Some(10));
        assert_eq!(response.assertions_until_exemption, Some(50));
    }

    #[test]
    fn test_response_from_status_graduated_agent() {
        let status = AdmissionStatus::new(0.7, 100, 0);
        let config = AdmissionConfig::default();

        let response = AdmissionStatusResponse::from_status("abc123".to_string(), &status, &config);

        assert_eq!(response.tier, TrustTierDto::Trusted);
        assert_eq!(response.pow_difficulty, 0);
        assert!(!response.pow_required);
        assert_eq!(response.assertions_until_reduced_difficulty, None);
        assert_eq!(response.assertions_until_exemption, None);
    }
}
