//! Platform Service Monitoring
//!
//! Monitors all platform services:
//! - Orchestrator
//! - CoreDNS
//! - Gitea
//! - OCI Registry
//! - Extension Registry
//! - Provisioning API Server

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use chrono::{DateTime, Utc, Duration};
use reqwest::Client;

/// Platform service status
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceStatus {
    pub name: String,
    pub service_type: ServiceType,
    pub status: HealthStatus,
    pub url: String,
    pub last_check: DateTime<Utc>,
    pub uptime_seconds: Option<u64>,
    pub metrics: ServiceMetrics,
    pub dependencies: Vec<String>,
    pub error_message: Option<String>,
}

/// Type of platform service
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ServiceType {
    Orchestrator,
    CoreDNS,
    Gitea,
    OciRegistry,
    ExtensionRegistry,
    ProvisioningApi,
    Database,
    Cache,
}

impl ServiceType {
    pub fn as_str(&self) -> &'static str {
        match self {
            ServiceType::Orchestrator => "orchestrator",
            ServiceType::CoreDNS => "coredns",
            ServiceType::Gitea => "gitea",
            ServiceType::OciRegistry => "oci_registry",
            ServiceType::ExtensionRegistry => "extension_registry",
            ServiceType::ProvisioningApi => "provisioning_api",
            ServiceType::Database => "database",
            ServiceType::Cache => "cache",
        }
    }
}

/// Health status of a service
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum HealthStatus {
    Healthy,
    Degraded,
    Unhealthy,
    Unknown,
}

/// Service-specific metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceMetrics {
    // Common metrics
    pub requests_per_second: Option<f64>,
    pub error_rate: Option<f64>,
    pub response_time_ms: Option<f64>,

    // Service-specific metrics
    pub custom: HashMap<String, String>,
}

impl Default for ServiceMetrics {
    fn default() -> Self {
        Self {
            requests_per_second: None,
            error_rate: None,
            response_time_ms: None,
            custom: HashMap::new(),
        }
    }
}

/// Platform monitor configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformMonitorConfig {
    pub orchestrator_url: String,
    pub coredns_url: Option<String>,
    pub gitea_url: Option<String>,
    pub oci_registry_url: Option<String>,
    pub extension_registry_url: Option<String>,
    pub provisioning_api_url: Option<String>,
    pub check_interval_seconds: u64,
    pub timeout_seconds: u64,
}

impl Default for PlatformMonitorConfig {
    fn default() -> Self {
        Self {
            orchestrator_url: "http://localhost:8080".to_string(),
            coredns_url: Some("http://localhost:9153".to_string()),
            gitea_url: Some("http://localhost:3000".to_string()),
            oci_registry_url: Some("http://localhost:5000".to_string()),
            extension_registry_url: Some("http://localhost:8081".to_string()),
            provisioning_api_url: Some("http://localhost:8082".to_string()),
            check_interval_seconds: 30,
            timeout_seconds: 5,
        }
    }
}

/// Platform monitor
pub struct PlatformMonitor {
    config: PlatformMonitorConfig,
    client: Client,
    service_statuses: Arc<RwLock<HashMap<ServiceType, ServiceStatus>>>,
    health_history: Arc<RwLock<Vec<HealthCheckRecord>>>,
}

/// Health check history record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckRecord {
    pub timestamp: DateTime<Utc>,
    pub service: ServiceType,
    pub status: HealthStatus,
    pub response_time_ms: u64,
}

impl PlatformMonitor {
    /// Create a new platform monitor
    pub fn new(config: PlatformMonitorConfig) -> Self {
        let client = Client::builder()
            .timeout(std::time::Duration::from_secs(config.timeout_seconds))
            .build()
            .unwrap();

        Self {
            config,
            client,
            service_statuses: Arc::new(RwLock::new(HashMap::new())),
            health_history: Arc::new(RwLock::new(Vec::new())),
        }
    }

    /// Start monitoring all services
    pub async fn start_monitoring(&self) {
        let interval = tokio::time::Duration::from_secs(self.config.check_interval_seconds);
        let mut ticker = tokio::time::interval(interval);

        loop {
            ticker.tick().await;
            self.check_all_services().await;
        }
    }

    /// Check all services health
    pub async fn check_all_services(&self) {
        // Check orchestrator
        self.check_orchestrator().await;

        // Check optional services
        if let Some(ref url) = self.config.coredns_url {
            self.check_coredns(url).await;
        }
        if let Some(ref url) = self.config.gitea_url {
            self.check_gitea(url).await;
        }
        if let Some(ref url) = self.config.oci_registry_url {
            self.check_oci_registry(url).await;
        }
        if let Some(ref url) = self.config.extension_registry_url {
            self.check_extension_registry(url).await;
        }
        if let Some(ref url) = self.config.provisioning_api_url {
            self.check_provisioning_api(url).await;
        }
    }

    /// Check orchestrator health
    async fn check_orchestrator(&self) {
        let start = std::time::Instant::now();
        let url = format!("{}/health", self.config.orchestrator_url);

        match self.client.get(&url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() {
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                // Try to get orchestrator stats
                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                if let Ok(stats_response) = self.client
                    .get(format!("{}/tasks", self.config.orchestrator_url))
                    .send()
                    .await
                {
                    if let Ok(tasks) = stats_response.json::<serde_json::Value>().await {
                        if let Some(tasks_array) = tasks.as_array() {
                            metrics.custom.insert(
                                "active_tasks".to_string(),
                                tasks_array.len().to_string(),
                            );
                        }
                    }
                }

                self.update_service_status(ServiceStatus {
                    name: "Orchestrator".to_string(),
                    service_type: ServiceType::Orchestrator,
                    status,
                    url: self.config.orchestrator_url.clone(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec![],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::Orchestrator, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "Orchestrator".to_string(),
                    service_type: ServiceType::Orchestrator,
                    status: HealthStatus::Unhealthy,
                    url: self.config.orchestrator_url.clone(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec![],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Check CoreDNS health
    async fn check_coredns(&self, url: &str) {
        let start = std::time::Instant::now();
        let health_url = format!("{}/health", url);

        match self.client.get(&health_url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() {
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                // Try to get CoreDNS metrics
                if let Ok(metrics_response) = self.client
                    .get(format!("{}/metrics", url))
                    .send()
                    .await
                {
                    if let Ok(text) = metrics_response.text().await {
                        // Parse Prometheus metrics (simplified)
                        if let Some(query_count) = Self::extract_metric(&text, "coredns_dns_requests_total") {
                            metrics.custom.insert("total_queries".to_string(), query_count);
                        }
                    }
                }

                self.update_service_status(ServiceStatus {
                    name: "CoreDNS".to_string(),
                    service_type: ServiceType::CoreDNS,
                    status,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec![],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::CoreDNS, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "CoreDNS".to_string(),
                    service_type: ServiceType::CoreDNS,
                    status: HealthStatus::Unhealthy,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec![],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Check Gitea health
    async fn check_gitea(&self, url: &str) {
        let start = std::time::Instant::now();
        let health_url = format!("{}/api/v1/version", url);

        match self.client.get(&health_url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() {
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                if let Ok(version_data) = response.json::<serde_json::Value>().await {
                    if let Some(version) = version_data.get("version").and_then(|v| v.as_str()) {
                        metrics.custom.insert("version".to_string(), version.to_string());
                    }
                }

                self.update_service_status(ServiceStatus {
                    name: "Gitea".to_string(),
                    service_type: ServiceType::Gitea,
                    status,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec!["database".to_string()],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::Gitea, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "Gitea".to_string(),
                    service_type: ServiceType::Gitea,
                    status: HealthStatus::Unhealthy,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec!["database".to_string()],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Check OCI registry health
    async fn check_oci_registry(&self, url: &str) {
        let start = std::time::Instant::now();
        let health_url = format!("{}/v2/", url);

        match self.client.get(&health_url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() || response.status().as_u16() == 401 {
                    // 401 is expected for registries requiring auth
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                self.update_service_status(ServiceStatus {
                    name: "OCI Registry".to_string(),
                    service_type: ServiceType::OciRegistry,
                    status,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec![],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::OciRegistry, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "OCI Registry".to_string(),
                    service_type: ServiceType::OciRegistry,
                    status: HealthStatus::Unhealthy,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec![],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Check extension registry health
    async fn check_extension_registry(&self, url: &str) {
        let start = std::time::Instant::now();
        let health_url = format!("{}/health", url);

        match self.client.get(&health_url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() {
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                self.update_service_status(ServiceStatus {
                    name: "Extension Registry".to_string(),
                    service_type: ServiceType::ExtensionRegistry,
                    status,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec!["cache".to_string()],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::ExtensionRegistry, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "Extension Registry".to_string(),
                    service_type: ServiceType::ExtensionRegistry,
                    status: HealthStatus::Unhealthy,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec!["cache".to_string()],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Check provisioning API health
    async fn check_provisioning_api(&self, url: &str) {
        let start = std::time::Instant::now();
        let health_url = format!("{}/health", url);

        match self.client.get(&health_url).send().await {
            Ok(response) => {
                let response_time = start.elapsed().as_millis() as u64;
                let status = if response.status().is_success() {
                    HealthStatus::Healthy
                } else {
                    HealthStatus::Degraded
                };

                let mut metrics = ServiceMetrics::default();
                metrics.response_time_ms = Some(response_time as f64);

                self.update_service_status(ServiceStatus {
                    name: "Provisioning API".to_string(),
                    service_type: ServiceType::ProvisioningApi,
                    status,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics,
                    dependencies: vec!["orchestrator".to_string()],
                    error_message: None,
                }).await;

                self.record_health_check(ServiceType::ProvisioningApi, status, response_time).await;
            }
            Err(e) => {
                self.update_service_status(ServiceStatus {
                    name: "Provisioning API".to_string(),
                    service_type: ServiceType::ProvisioningApi,
                    status: HealthStatus::Unhealthy,
                    url: url.to_string(),
                    last_check: Utc::now(),
                    uptime_seconds: None,
                    metrics: ServiceMetrics::default(),
                    dependencies: vec!["orchestrator".to_string()],
                    error_message: Some(e.to_string()),
                }).await;
            }
        }
    }

    /// Get status of all services
    pub async fn get_all_statuses(&self) -> HashMap<ServiceType, ServiceStatus> {
        self.service_statuses.read().await.clone()
    }

    /// Get status of a specific service
    pub async fn get_service_status(&self, service_type: ServiceType) -> Option<ServiceStatus> {
        self.service_statuses.read().await.get(&service_type).cloned()
    }

    /// Get health history for a service
    pub async fn get_health_history(
        &self,
        service_type: ServiceType,
        since: DateTime<Utc>,
    ) -> Vec<HealthCheckRecord> {
        self.health_history.read().await
            .iter()
            .filter(|record| record.service == service_type && record.timestamp >= since)
            .cloned()
            .collect()
    }

    /// Get service dependency graph
    pub async fn get_dependency_graph(&self) -> HashMap<ServiceType, Vec<String>> {
        let statuses = self.service_statuses.read().await;
        let mut graph = HashMap::new();

        for (service_type, status) in statuses.iter() {
            graph.insert(*service_type, status.dependencies.clone());
        }

        graph
    }

    // Helper methods

    async fn update_service_status(&self, status: ServiceStatus) {
        let mut statuses = self.service_statuses.write().await;
        statuses.insert(status.service_type, status);
    }

    async fn record_health_check(&self, service: ServiceType, status: HealthStatus, response_time_ms: u64) {
        let mut history = self.health_history.write().await;
        history.push(HealthCheckRecord {
            timestamp: Utc::now(),
            service,
            status,
            response_time_ms,
        });

        // Keep only last 1000 records
        if history.len() > 1000 {
            history.drain(0..100);
        }
    }

    fn extract_metric(text: &str, metric_name: &str) -> Option<String> {
        // Simplified Prometheus metric parsing
        for line in text.lines() {
            if line.starts_with(metric_name) {
                if let Some(value) = line.split_whitespace().last() {
                    return Some(value.to_string());
                }
            }
        }
        None
    }
}

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

    #[tokio::test]
    async fn test_platform_monitor_creation() {
        let config = PlatformMonitorConfig::default();
        let monitor = PlatformMonitor::new(config);

        let statuses = monitor.get_all_statuses().await;
        assert!(statuses.is_empty());  // No checks run yet
    }
}
