//! Health check module for monitoring system health and readiness
//!
//! This module provides comprehensive health check endpoints that monitor:
//! - Basic liveness (is the service running?)
//! - Readiness (can the service handle requests?)
//! - Database connectivity
//! - External service dependencies
//! - System resources

use std::collections::HashMap;
use std::time::Instant;

use axum::{extract::State, http::StatusCode, response::Json, routing::get, Router};
use serde::{Deserialize, Serialize};
#[cfg(any(feature = "auth", feature = "content-db"))]
use sqlx::PgPool;
use tracing::debug;
#[cfg(any(feature = "auth", feature = "content-db"))]
use tracing::error;

// Service imports removed as they're not directly used in health checks
use crate::app_state::AppState;

/// Health check status levels
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum HealthStatus {
    /// Service is healthy and fully operational
    Healthy,
    /// Service is degraded but still functional
    Degraded,
    /// Service is unhealthy and may not function properly
    Unhealthy,
}

/// Individual component health check result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentHealth {
    /// Component name
    pub name: String,
    /// Health status
    pub status: HealthStatus,
    /// Optional status message
    pub message: Option<String>,
    /// Response time in milliseconds
    pub response_time_ms: u64,
    /// Additional metadata
    pub metadata: HashMap<String, serde_json::Value>,
}

/// Overall system health response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
    /// Overall system status
    pub status: HealthStatus,
    /// Timestamp of health check
    pub timestamp: String,
    /// Service version
    pub version: String,
    /// Environment
    pub environment: String,
    /// Uptime in seconds
    pub uptime_seconds: u64,
    /// Individual component health
    pub components: Vec<ComponentHealth>,
    /// Summary of component statuses
    pub summary: HashMap<String, i32>,
}

/// Liveness probe response (simple check)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LivenessResponse {
    /// Service status
    pub status: HealthStatus,
    /// Timestamp
    pub timestamp: String,
    /// Simple message
    pub message: String,
}

/// Readiness probe response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadinessResponse {
    /// Service readiness status
    pub status: HealthStatus,
    /// Timestamp
    pub timestamp: String,
    /// Ready components count
    pub ready_components: i32,
    /// Total components count
    pub total_components: i32,
    /// Component statuses
    pub components: Vec<String>,
}

/// Health check service
pub struct HealthService {
    start_time: Instant,
}

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

impl HealthService {
    /// Create a new health service
    pub fn new() -> Self {
        Self {
            start_time: Instant::now(),
        }
    }

    /// Get service uptime in seconds
    pub fn uptime_seconds(&self) -> u64 {
        self.start_time.elapsed().as_secs()
    }

    /// Check database health
    #[cfg(any(feature = "auth", feature = "content-db"))]
    #[allow(dead_code)]
    async fn check_database(&self, pool: &PgPool) -> ComponentHealth {
        let start = Instant::now();

        match sqlx::query("SELECT 1").execute(pool).await {
            Ok(_) => {
                let response_time = start.elapsed().as_millis() as u64;

                // Check pool status
                let mut metadata = HashMap::new();
                metadata.insert(
                    "pool_size".to_string(),
                    serde_json::Value::Number(pool.size().into()),
                );
                metadata.insert(
                    "idle_connections".to_string(),
                    serde_json::Value::Number(pool.num_idle().into()),
                );

                // Warn if response time is high
                let status = if response_time > 1000 {
                    HealthStatus::Degraded
                } else {
                    HealthStatus::Healthy
                };

                ComponentHealth {
                    name: "database".to_string(),
                    status,
                    message: Some("Database connection successful".to_string()),
                    response_time_ms: response_time,
                    metadata,
                }
            }
            Err(e) => {
                error!("Database health check failed: {}", e);
                ComponentHealth {
                    name: "database".to_string(),
                    status: HealthStatus::Unhealthy,
                    message: Some(format!("Database connection failed: {}", e)),
                    response_time_ms: start.elapsed().as_millis() as u64,
                    metadata: HashMap::new(),
                }
            }
        }
    }

    /// Check authentication service health (basic check)
    #[cfg(feature = "auth")]
    async fn check_auth_service_basic(&self) -> ComponentHealth {
        let start = Instant::now();

        // Basic health check - just verify service is configured
        ComponentHealth {
            name: "auth_service".to_string(),
            status: HealthStatus::Healthy,
            message: Some("Authentication service configured".to_string()),
            response_time_ms: start.elapsed().as_millis() as u64,
            metadata: HashMap::new(),
        }
    }

    /// Check content service health (basic check)
    #[cfg(feature = "content-db")]
    async fn check_content_service_basic(&self) -> ComponentHealth {
        let start = Instant::now();

        // Basic health check - just verify service is configured
        ComponentHealth {
            name: "content_service".to_string(),
            status: HealthStatus::Healthy,
            message: Some("Content service configured".to_string()),
            response_time_ms: start.elapsed().as_millis() as u64,
            metadata: HashMap::new(),
        }
    }

    /// Check email service health (basic check)
    #[cfg(feature = "email")]
    async fn check_email_service_basic(&self) -> ComponentHealth {
        let start = Instant::now();

        // Basic health check - just verify service is configured
        ComponentHealth {
            name: "email_service".to_string(),
            status: HealthStatus::Healthy,
            message: Some("Email service configured".to_string()),
            response_time_ms: start.elapsed().as_millis() as u64,
            metadata: HashMap::new(),
        }
    }

    /// Check system resources
    fn check_system_resources(&self) -> ComponentHealth {
        let start = Instant::now();

        let mut metadata = HashMap::new();

        // Check memory usage (simplified)
        // In a real implementation, you might use a crate like `sysinfo`
        metadata.insert(
            "memory_check".to_string(),
            serde_json::Value::String("basic".to_string()),
        );

        // Check disk space for critical directories
        let critical_dirs = vec!["/tmp", "/var/log", "./uploads", "./logs"];
        let mut disk_warnings = Vec::new();

        for dir in critical_dirs {
            if let Ok(metadata_fs) = std::fs::metadata(dir) {
                if metadata_fs.is_dir() {
                    // Directory exists, assume it's healthy
                    // In production, you'd check actual disk usage
                    continue;
                }
            }
            disk_warnings.push(format!("Directory {dir} not accessible"));
        }

        let status = if disk_warnings.is_empty() {
            HealthStatus::Healthy
        } else {
            HealthStatus::Degraded
        };

        metadata.insert(
            "disk_warnings".to_string(),
            serde_json::Value::Array(
                disk_warnings
                    .into_iter()
                    .map(serde_json::Value::String)
                    .collect(),
            ),
        );

        ComponentHealth {
            name: "system_resources".to_string(),
            status,
            message: Some("System resources checked".to_string()),
            response_time_ms: start.elapsed().as_millis() as u64,
            metadata,
        }
    }

    /// Perform comprehensive health check
    #[allow(clippy::vec_init_then_push)]
    pub async fn check_health(&self, _state: &AppState) -> HealthResponse {
        let mut components = Vec::new();

        // Check auth service
        #[cfg(feature = "auth")]
        {
            components.push(self.check_auth_service_basic().await);
        }

        // Check content service
        #[cfg(feature = "content-db")]
        {
            components.push(self.check_content_service_basic().await);
        }

        // Check email service
        #[cfg(feature = "email")]
        {
            components.push(self.check_email_service_basic().await);
        }

        // Check system resources
        components.push(self.check_system_resources());

        // Calculate overall status
        let overall_status = self.calculate_overall_status(&components);

        // Create summary
        let mut summary = HashMap::new();
        for component in &components {
            let status_str = match component.status {
                HealthStatus::Healthy => "healthy",
                HealthStatus::Degraded => "degraded",
                HealthStatus::Unhealthy => "unhealthy",
            };
            *summary.entry(status_str.to_string()).or_insert(0) += 1;
        }

        HealthResponse {
            status: overall_status,
            timestamp: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs()
                .to_string(),
            version: env!("CARGO_PKG_VERSION").to_string(),
            environment: std::env::var("ENVIRONMENT").unwrap_or_else(|_| "development".to_string()),
            uptime_seconds: self.uptime_seconds(),
            components,
            summary,
        }
    }

    /// Calculate overall health status based on components
    fn calculate_overall_status(&self, components: &[ComponentHealth]) -> HealthStatus {
        let mut has_unhealthy = false;
        let mut has_degraded = false;

        for component in components {
            match component.status {
                HealthStatus::Unhealthy => has_unhealthy = true,
                HealthStatus::Degraded => has_degraded = true,
                HealthStatus::Healthy => {}
            }
        }

        if has_unhealthy {
            HealthStatus::Unhealthy
        } else if has_degraded {
            HealthStatus::Degraded
        } else {
            HealthStatus::Healthy
        }
    }

    /// Simple liveness check
    pub fn check_liveness(&self) -> LivenessResponse {
        LivenessResponse {
            status: HealthStatus::Healthy,
            timestamp: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs()
                .to_string(),
            message: "Service is alive".to_string(),
        }
    }

    /// Check readiness
    pub async fn check_readiness(&self, state: &AppState) -> ReadinessResponse {
        let health = self.check_health(state).await;

        let ready_components = health
            .components
            .iter()
            .filter(|c| c.status == HealthStatus::Healthy)
            .count() as i32;

        let total_components = health.components.len() as i32;

        let status = if ready_components == total_components {
            HealthStatus::Healthy
        } else if ready_components > 0 {
            HealthStatus::Degraded
        } else {
            HealthStatus::Unhealthy
        };

        ReadinessResponse {
            status,
            timestamp: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs()
                .to_string(),
            ready_components,
            total_components,
            components: health
                .components
                .iter()
                .map(|c| format!("{}: {:?}", c.name, c.status))
                .collect(),
        }
    }
}

/// Health check handlers
pub mod handlers {
    use super::*;

    /// Comprehensive health check endpoint
    pub async fn health_check(
        State(state): State<AppState>,
    ) -> Result<Json<HealthResponse>, StatusCode> {
        let health_service = HealthService::new();
        let health = health_service.check_health(&state).await;

        let status_code = match health.status {
            HealthStatus::Healthy => StatusCode::OK,
            HealthStatus::Degraded => StatusCode::OK, // Still serving traffic
            HealthStatus::Unhealthy => StatusCode::SERVICE_UNAVAILABLE,
        };

        debug!("Health check completed: {:?}", health.status);

        if status_code == StatusCode::SERVICE_UNAVAILABLE {
            Err(status_code)
        } else {
            Ok(Json(health))
        }
    }

    /// Liveness probe endpoint (Kubernetes compatible)
    pub async fn liveness_probe(_state: State<AppState>) -> Json<LivenessResponse> {
        let health_service = HealthService::new();
        Json(health_service.check_liveness())
    }

    /// Readiness probe endpoint (Kubernetes compatible)
    pub async fn readiness_probe(
        State(state): State<AppState>,
    ) -> Result<Json<ReadinessResponse>, StatusCode> {
        let health_service = HealthService::new();
        let readiness = health_service.check_readiness(&state).await;

        let status_code = match readiness.status {
            HealthStatus::Healthy => StatusCode::OK,
            HealthStatus::Degraded => StatusCode::OK, // Still serving some traffic
            HealthStatus::Unhealthy => StatusCode::SERVICE_UNAVAILABLE,
        };

        debug!("Readiness check completed: {:?}", readiness.status);

        if status_code == StatusCode::SERVICE_UNAVAILABLE {
            Err(status_code)
        } else {
            Ok(Json(readiness))
        }
    }
}

/// Create health check routes
pub fn create_health_routes() -> Router<AppState> {
    Router::new()
        .route("/health", get(handlers::health_check))
        .route("/health/live", get(handlers::liveness_probe))
        .route("/health/ready", get(handlers::readiness_probe))
}

// Extension traits for existing services to add health checks

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

    #[test]
    fn test_health_status_serialization() {
        let status = HealthStatus::Healthy;
        let json = serde_json::to_string(&status).unwrap();
        assert_eq!(json, "\"healthy\"");
    }

    #[test]
    fn test_component_health_creation() {
        let component = ComponentHealth {
            name: "test".to_string(),
            status: HealthStatus::Healthy,
            message: Some("Test message".to_string()),
            response_time_ms: 100,
            metadata: HashMap::new(),
        };

        assert_eq!(component.name, "test");
        assert_eq!(component.status, HealthStatus::Healthy);
        assert_eq!(component.response_time_ms, 100);
    }

    #[test]
    fn test_overall_status_calculation() {
        let health_service = HealthService::new();

        let components = vec![
            ComponentHealth {
                name: "test1".to_string(),
                status: HealthStatus::Healthy,
                message: None,
                response_time_ms: 100,
                metadata: HashMap::new(),
            },
            ComponentHealth {
                name: "test2".to_string(),
                status: HealthStatus::Healthy,
                message: None,
                response_time_ms: 200,
                metadata: HashMap::new(),
            },
        ];

        let overall = health_service.calculate_overall_status(&components);
        assert_eq!(overall, HealthStatus::Healthy);

        let components_with_degraded = vec![
            ComponentHealth {
                name: "test1".to_string(),
                status: HealthStatus::Healthy,
                message: None,
                response_time_ms: 100,
                metadata: HashMap::new(),
            },
            ComponentHealth {
                name: "test2".to_string(),
                status: HealthStatus::Degraded,
                message: None,
                response_time_ms: 200,
                metadata: HashMap::new(),
            },
        ];

        let overall_degraded = health_service.calculate_overall_status(&components_with_degraded);
        assert_eq!(overall_degraded, HealthStatus::Degraded);
    }
}
