//! Monitoring Service - Phase 3.4
//!
//! Aggregates metrics from rotation scheduler, secret sharing,
//! and secrets service to provide real-time dashboard data.

use std::sync::Arc;

use chrono::{Duration, Utc};
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use tracing::debug;

use crate::error::Result;

/// Expiring secret alert
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExpiringSecretAlert {
    pub path: String,
    pub domain: String,
    pub days_remaining: i32,
    pub severity: AlertSeverity,
}

/// Alert severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AlertSeverity {
    #[serde(rename = "critical")]
    Critical,
    #[serde(rename = "warning")]
    Warning,
    #[serde(rename = "info")]
    Info,
}

/// Failed access attempt record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailedAccessRecord {
    pub timestamp: String,
    pub user: String,
    pub secret_path: String,
    pub reason: String,
    pub workspace: String,
}

/// Dashboard metrics snapshot
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardMetrics {
    /// Secrets expiring in the next 30 days
    pub expiring_secrets: Vec<ExpiringSecretAlert>,

    /// Failed access attempts in the last 24 hours
    pub failed_access_attempts: Vec<FailedAccessRecord>,

    /// Rotation compliance metrics
    pub rotation_metrics: RotationComplianceMetrics,

    /// Secret sharing metrics
    pub sharing_metrics: SharingComplianceMetrics,

    /// Total secret count
    pub total_secrets: usize,

    /// Temporal secrets count
    pub temporal_secrets: usize,

    /// Permanent secrets count
    pub permanent_secrets: usize,

    /// Last update timestamp
    pub last_updated: String,
}

/// Rotation compliance metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RotationComplianceMetrics {
    pub total: usize,
    pub completed: usize,
    pub pending: usize,
    pub failed: usize,
    pub disabled: usize,
    pub compliance_rate: f64,
}

/// Sharing compliance metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SharingComplianceMetrics {
    pub active_grants: usize,
    pub pending_grants: usize,
    pub revoked_grants: usize,
    pub total_accesses: usize,
    pub failed_accesses: usize,
}

/// Monitoring Service for real-time metrics
pub struct MonitoringService {
    /// Cached metrics updated periodically
    metrics_cache: Arc<RwLock<Option<DashboardMetrics>>>,

    /// Failed access log
    failed_accesses: Arc<RwLock<Vec<FailedAccessRecord>>>,

    /// Expiring secrets cache
    expiring_secrets: Arc<RwLock<Vec<ExpiringSecretAlert>>>,
}

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

impl MonitoringService {
    /// Create new monitoring service
    pub fn new() -> Self {
        Self {
            metrics_cache: Arc::new(RwLock::new(None)),
            failed_accesses: Arc::new(RwLock::new(Vec::new())),
            expiring_secrets: Arc::new(RwLock::new(Vec::new())),
        }
    }

    /// Get current dashboard metrics (from cache)
    pub async fn get_dashboard_metrics(&self) -> Result<DashboardMetrics> {
        let cache = self.metrics_cache.read().await;

        if let Some(metrics) = cache.clone() {
            Ok(metrics)
        } else {
            // Return default empty metrics if not yet calculated
            Ok(DashboardMetrics {
                expiring_secrets: Vec::new(),
                failed_access_attempts: Vec::new(),
                rotation_metrics: RotationComplianceMetrics {
                    total: 0,
                    completed: 0,
                    pending: 0,
                    failed: 0,
                    disabled: 0,
                    compliance_rate: 0.0,
                },
                sharing_metrics: SharingComplianceMetrics {
                    active_grants: 0,
                    pending_grants: 0,
                    revoked_grants: 0,
                    total_accesses: 0,
                    failed_accesses: 0,
                },
                total_secrets: 0,
                temporal_secrets: 0,
                permanent_secrets: 0,
                last_updated: Utc::now().to_rfc3339(),
            })
        }
    }

    /// Record a failed access attempt
    pub async fn record_failed_access(
        &self,
        user: &str,
        secret_path: &str,
        reason: &str,
        workspace: &str,
    ) -> Result<()> {
        let record = FailedAccessRecord {
            timestamp: Utc::now().to_rfc3339(),
            user: user.to_string(),
            secret_path: secret_path.to_string(),
            reason: reason.to_string(),
            workspace: workspace.to_string(),
        };

        let mut accesses = self.failed_accesses.write().await;

        // Keep only last 24 hours of records
        let cutoff = Utc::now() - Duration::days(1);
        accesses.retain(|r| {
            if let Ok(ts) = chrono::DateTime::parse_from_rfc3339(&r.timestamp) {
                ts > cutoff.with_timezone(&ts.timezone())
            } else {
                false
            }
        });

        accesses.push(record);

        debug!("Failed access recorded");

        Ok(())
    }

    /// Add expiring secret alert
    pub async fn add_expiring_secret_alert(&self, alert: ExpiringSecretAlert) -> Result<()> {
        let mut secrets = self.expiring_secrets.write().await;

        // Remove if already exists
        secrets.retain(|s| s.path != alert.path);

        // Only keep alerts for secrets expiring within 30 days
        if alert.days_remaining <= 30 && alert.days_remaining > 0 {
            secrets.push(alert);

            // Sort by days remaining (critical first)
            secrets.sort_by(|a, b| a.days_remaining.cmp(&b.days_remaining));
        }

        debug!(count = %secrets.len(), "Expiring secrets updated");

        Ok(())
    }

    /// Get expiring secrets
    pub async fn get_expiring_secrets(&self) -> Result<Vec<ExpiringSecretAlert>> {
        let secrets = self.expiring_secrets.read().await;
        Ok(secrets.clone())
    }

    /// Get failed access attempts (last 24 hours)
    pub async fn get_failed_access_attempts(&self) -> Result<Vec<FailedAccessRecord>> {
        let accesses = self.failed_accesses.read().await;
        Ok(accesses.clone())
    }

    /// Update dashboard metrics (call periodically)
    pub async fn update_metrics(
        &self,
        total_secrets: usize,
        temporal_secrets: usize,
        permanent_secrets: usize,
        rotation_metrics: RotationComplianceMetrics,
        sharing_metrics: SharingComplianceMetrics,
    ) -> Result<()> {
        let expiring = self.expiring_secrets.read().await.clone();
        let failed_accesses = self.failed_accesses.read().await.clone();

        let metrics = DashboardMetrics {
            expiring_secrets: expiring,
            failed_access_attempts: failed_accesses,
            rotation_metrics,
            sharing_metrics,
            total_secrets,
            temporal_secrets,
            permanent_secrets,
            last_updated: Utc::now().to_rfc3339(),
        };

        let mut cache = self.metrics_cache.write().await;
        *cache = Some(metrics);

        Ok(())
    }

    /// Get alert summary
    pub async fn get_alert_summary(&self) -> Result<AlertSummary> {
        let expiring = self.expiring_secrets.read().await;
        let failed_accesses = self.failed_accesses.read().await;

        let critical_count = expiring
            .iter()
            .filter(|s| s.severity == AlertSeverity::Critical)
            .count();
        let warning_count = expiring
            .iter()
            .filter(|s| s.severity == AlertSeverity::Warning)
            .count();

        Ok(AlertSummary {
            critical_alerts: critical_count,
            warning_alerts: warning_count,
            failed_accesses_24h: failed_accesses.len(),
            total_alerts: critical_count + warning_count + failed_accesses.len(),
        })
    }
}

/// Alert summary statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertSummary {
    pub critical_alerts: usize,
    pub warning_alerts: usize,
    pub failed_accesses_24h: usize,
    pub total_alerts: usize,
}

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

    #[tokio::test]
    async fn test_record_failed_access() {
        let service = MonitoringService::new();
        service
            .record_failed_access("alice", "secret/path", "Unauthorized", "workspace1")
            .await
            .expect("Should record access");

        let accesses = service
            .get_failed_access_attempts()
            .await
            .expect("Should get accesses");
        assert_eq!(accesses.len(), 1);
        assert_eq!(accesses[0].user, "alice");
    }

    #[tokio::test]
    async fn test_expiring_secrets_alert() {
        let service = MonitoringService::new();

        let alert = ExpiringSecretAlert {
            path: "workspace/domain/secret".to_string(),
            domain: "domain".to_string(),
            days_remaining: 7,
            severity: AlertSeverity::Warning,
        };

        service
            .add_expiring_secret_alert(alert.clone())
            .await
            .expect("Should add alert");

        let secrets = service
            .get_expiring_secrets()
            .await
            .expect("Should get secrets");
        assert_eq!(secrets.len(), 1);
        assert_eq!(secrets[0].path, alert.path);
    }

    #[tokio::test]
    async fn test_alert_summary() {
        let service = MonitoringService::new();

        // Add some alerts
        let critical_alert = ExpiringSecretAlert {
            path: "critical".to_string(),
            domain: "critical".to_string(),
            days_remaining: 1,
            severity: AlertSeverity::Critical,
        };
        service
            .add_expiring_secret_alert(critical_alert)
            .await
            .expect("Should add alert");

        // Record failed access
        service
            .record_failed_access("user", "path", "reason", "ws")
            .await
            .expect("Should record");

        let summary = service
            .get_alert_summary()
            .await
            .expect("Should get summary");
        assert_eq!(summary.critical_alerts, 1);
        assert_eq!(summary.failed_accesses_24h, 1);
        assert_eq!(summary.total_alerts, 2);
    }
}
