//! API Key Rotation Module
//!
//! Implements forced rotation mechanism with grace periods

use crate::core::api_key::models::*;
use chrono::{Duration, Utc};
use std::collections::HashMap;
use tokio::sync::RwLock;

pub struct RotationManager {
    grace_periods: HashMap<ApiKeyType, i64>,
    rotation_queue: RwLock<Vec<RotationTask>>,
}

#[derive(Debug, Clone)]
struct RotationTask {
    key_id: String,
    key_type: ApiKeyType,
    reason: RotationReason,
    created_at: chrono::DateTime<Utc>,
    scheduled_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RotationReason {
    Expired,
    Manual,
    Forced,
    AnomalyDetected,
    SecurityBreach,
    PolicyChange,
}

impl RotationReason {
    pub fn as_str(&self) -> &'static str {
        match self {
            RotationReason::Expired => "expired",
            RotationReason::Manual => "manual",
            RotationReason::Forced => "forced",
            RotationReason::AnomalyDetected => "anomaly_detected",
            RotationReason::SecurityBreach => "security_breach",
            RotationReason::PolicyChange => "policy_change",
        }
    }

    pub fn requires_immediate_rotation(&self) -> bool {
        matches!(
            self,
            RotationReason::AnomalyDetected | RotationReason::SecurityBreach
        )
    }
}

impl RotationManager {
    pub fn new() -> Self {
        let mut grace_periods = HashMap::new();
        grace_periods.insert(ApiKeyType::System, 72);
        grace_periods.insert(ApiKeyType::User, 24);
        grace_periods.insert(ApiKeyType::Service, 48);
        grace_periods.insert(ApiKeyType::Integration, 24);
        grace_periods.insert(ApiKeyType::Emergency, 0);

        Self {
            grace_periods,
            rotation_queue: RwLock::new(Vec::new()),
        }
    }

    pub fn get_grace_period(&self, key_type: ApiKeyType) -> Duration {
        let hours = self.grace_periods.get(&key_type).copied().unwrap_or(24);
        Duration::hours(hours)
    }

    pub fn calculate_grace_period_end(
        &self,
        key_type: ApiKeyType,
        triggered_at: chrono::DateTime<Utc>,
    ) -> chrono::DateTime<Utc> {
        triggered_at + self.get_grace_period(key_type)
    }

    pub fn is_in_grace_period(&self, grace_period_end: Option<chrono::DateTime<Utc>>) -> bool {
        match grace_period_end {
            Some(end) => Utc::now() < end,
            None => false,
        }
    }

    pub fn is_grace_period_expired(&self, grace_period_end: Option<chrono::DateTime<Utc>>) -> bool {
        match grace_period_end {
            Some(end) => Utc::now() >= end,
            None => true,
        }
    }

    pub async fn queue_rotation(
        &self,
        key_id: String,
        key_type: ApiKeyType,
        reason: RotationReason,
    ) {
        let grace_period_end = self.calculate_grace_period_end(key_type, Utc::now());

        let scheduled_at = if reason.requires_immediate_rotation() {
            Utc::now()
        } else {
            grace_period_end
        };

        let task = RotationTask {
            key_id,
            key_type,
            reason,
            created_at: Utc::now(),
            scheduled_at,
        };

        let mut queue = self.rotation_queue.write().await;
        queue.push(task);
    }

    pub async fn get_pending_rotations(&self) -> Vec<RotationStatus> {
        let queue = self.rotation_queue.read().await;

        queue
            .iter()
            .map(|task| {
                let grace_period_end =
                    self.calculate_grace_period_end(task.key_type, task.created_at);
                RotationStatus {
                    key_id: task.key_id.clone(),
                    requires_rotation: true,
                    reason: Some(task.reason.as_str().to_string()),
                    grace_period_end: Some(grace_period_end),
                    in_grace_period: self.is_in_grace_period(Some(grace_period_end)),
                }
            })
            .collect()
    }

    pub async fn get_overdue_rotations(&self) -> Vec<RotationStatus> {
        let queue = self.rotation_queue.read().await;
        let now = Utc::now();

        queue
            .iter()
            .filter(|task| task.scheduled_at <= now)
            .map(|task| {
                let grace_period_end =
                    self.calculate_grace_period_end(task.key_type, task.created_at);
                RotationStatus {
                    key_id: task.key_id.clone(),
                    requires_rotation: true,
                    reason: Some(task.reason.as_str().to_string()),
                    grace_period_end: Some(grace_period_end),
                    in_grace_period: self.is_in_grace_period(Some(grace_period_end)),
                }
            })
            .collect()
    }

    pub async fn remove_from_queue(&self, key_id: &str) {
        let mut queue = self.rotation_queue.write().await;
        queue.retain(|task| task.key_id != key_id);
    }

    pub fn check_rotation_required(&self, key: &ApiKey) -> RotationStatus {
        let now = Utc::now();

        if key.status == ApiKeyStatus::Revoked {
            return RotationStatus {
                key_id: key.key_id.clone(),
                requires_rotation: false,
                reason: Some("key_revoked".to_string()),
                grace_period_end: None,
                in_grace_period: false,
            };
        }

        if let Some(expires_at) = key.expires_at {
            if now > expires_at {
                return RotationStatus {
                    key_id: key.key_id.clone(),
                    requires_rotation: true,
                    reason: Some("expired".to_string()),
                    grace_period_end: Some(
                        self.calculate_grace_period_end(key.key_type, expires_at),
                    ),
                    in_grace_period: self.is_in_grace_period(Some(
                        self.calculate_grace_period_end(key.key_type, expires_at),
                    )),
                };
            }
        }

        if key.rotation_required {
            return RotationStatus {
                key_id: key.key_id.clone(),
                requires_rotation: true,
                reason: key.rotation_reason.clone(),
                grace_period_end: key.grace_period_end,
                in_grace_period: self.is_in_grace_period(key.grace_period_end),
            };
        }

        RotationStatus {
            key_id: key.key_id.clone(),
            requires_rotation: false,
            reason: None,
            grace_period_end: None,
            in_grace_period: false,
        }
    }

    pub fn should_auto_expire(&self, key: &ApiKey) -> bool {
        if key.status != ApiKeyStatus::Active {
            return false;
        }

        if key.key_type == ApiKeyType::Emergency {
            if let Some(expires_at) = key.expires_at {
                return Utc::now() >= expires_at;
            }
        }

        if let Some(grace_period_end) = key.grace_period_end {
            return self.is_grace_period_expired(Some(grace_period_end)) && key.rotation_required;
        }

        false
    }
}

impl Default for RotationManager {
    fn default() -> Self {
        Self::new()
    }
}

pub struct RotationScheduler {
    check_interval_seconds: u64,
}

impl RotationScheduler {
    pub fn new(check_interval_seconds: u64) -> Self {
        Self {
            check_interval_seconds,
        }
    }

    pub fn check_interval(&self) -> Duration {
        Duration::seconds(self.check_interval_seconds as i64)
    }
}

impl Default for RotationScheduler {
    fn default() -> Self {
        Self::new(3600)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_grace_period_calculation() {
        let manager = RotationManager::new();

        let user_grace = manager.get_grace_period(ApiKeyType::User);
        assert_eq!(user_grace, Duration::hours(24));

        let system_grace = manager.get_grace_period(ApiKeyType::System);
        assert_eq!(system_grace, Duration::hours(72));

        let emergency_grace = manager.get_grace_period(ApiKeyType::Emergency);
        assert_eq!(emergency_grace, Duration::hours(0));
    }

    #[test]
    fn test_rotation_reason_requires_immediate() {
        assert!(RotationReason::AnomalyDetected.requires_immediate_rotation());
        assert!(RotationReason::SecurityBreach.requires_immediate_rotation());
        assert!(!RotationReason::Expired.requires_immediate_rotation());
        assert!(!RotationReason::Manual.requires_immediate_rotation());
        assert!(!RotationReason::Forced.requires_immediate_rotation());
        assert!(!RotationReason::PolicyChange.requires_immediate_rotation());
    }

    #[tokio::test]
    async fn test_queue_rotation() {
        let manager = RotationManager::new();

        manager
            .queue_rotation(
                "test_key_123".to_string(),
                ApiKeyType::User,
                RotationReason::Manual,
            )
            .await;

        let pending = manager.get_pending_rotations().await;
        assert_eq!(pending.len(), 1);
        assert_eq!(pending[0].key_id, "test_key_123");
    }

    #[test]
    fn test_is_in_grace_period() {
        let manager = RotationManager::new();

        let future_end = Utc::now() + Duration::hours(12);
        assert!(manager.is_in_grace_period(Some(future_end)));

        let past_end = Utc::now() - Duration::hours(1);
        assert!(!manager.is_in_grace_period(Some(past_end)));

        assert!(!manager.is_in_grace_period(None));
    }
}
