//! Health monitoring and diagnostics for persistence layer
//!
//! This module provides comprehensive health checking and monitoring
//! for all database systems in the trading platform.

use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
use thiserror::Error;
use tokio::time::timeout;

use super::{
    clickhouse::ClickHouseClient, influxdb::InfluxClient, postgres::PostgresPool, redis::RedisPool,
};

/// Health check errors
#[derive(Debug, Error)]
/// HealthError
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum HealthError {
    #[error("PostgreSQL health check failed: {0}")]
    // Postgres variant
    Postgres(String),
    #[error("InfluxDB health check failed: {0}")]
    // Influx variant
    Influx(String),
    #[error("Redis health check failed: {0}")]
    // Redis variant
    Redis(String),
    #[error("ClickHouse health check failed: {0}")]
    // ClickHouse variant
    ClickHouse(String),
    #[error("Health check timeout: {0}")]
    // Timeout variant
    Timeout(String),
}

/// Overall health status for the persistence layer
#[derive(Debug, Clone, Serialize, Deserialize)]
/// HealthStatus
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct HealthStatus {
    /// Overall Status
    pub overall_status: SystemStatus,
    /// Postgres
    pub postgres: ComponentHealth,
    /// Influx
    pub influx: ComponentHealth,
    /// `Redis`
    pub redis: ComponentHealth,
    /// Clickhouse
    pub clickhouse: Option<ComponentHealth>,
    /// Check Timestamp
    pub check_timestamp: u64,
    /// Check `Duration` Ms
    pub check_duration_ms: u64,
}

/// Health status for individual components
#[derive(Debug, Clone, Serialize, Deserialize)]
/// ComponentHealth
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct ComponentHealth {
    /// Status
    pub status: SystemStatus,
    /// Latency Ms
    pub latency_ms: f64,
    /// Error Message
    pub error_message: Option<String>,
    /// Last Successful Check
    pub last_successful_check: Option<u64>,
    /// Consecutive Failures
    pub consecutive_failures: u32,
}

/// System status enumeration
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
/// SystemStatus
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum SystemStatus {
    // Healthy variant
    Healthy,
    // Degraded variant
    Degraded,
    // Unhealthy variant
    Unhealthy,
    // Unknown variant
    Unknown,
}

impl SystemStatus {
    /// Check if the status indicates the system is operational
    pub const fn is_operational(&self) -> bool {
        matches!(self, SystemStatus::Healthy | SystemStatus::Degraded)
    }
}

/// Health monitoring coordinator
#[derive(Debug)]
pub struct PersistenceHealth {
    enabled: bool,
    timeout_duration: Duration,
}

impl PersistenceHealth {
    /// Create a new health monitor
    pub const fn new(enabled: bool, _check_interval: Duration) -> Self {
        Self {
            enabled,
            timeout_duration: Duration::from_millis(5000), // 5 second timeout
        }
    }

    /// Perform comprehensive health check on all systems
    pub async fn check_all_systems(
        &self,
        postgres: &PostgresPool,
        influx: &InfluxClient,
        redis: &RedisPool,
        clickhouse: Option<&ClickHouseClient>,
    ) -> Result<HealthStatus, HealthError> {
        if !self.enabled {
            return Ok(HealthStatus {
                overall_status: SystemStatus::Unknown,
                postgres: ComponentHealth::unknown(),
                influx: ComponentHealth::unknown(),
                redis: ComponentHealth::unknown(),
                clickhouse: clickhouse.map(|_| ComponentHealth::unknown()),
                check_timestamp: chrono::Utc::now().timestamp() as u64,
                check_duration_ms: 0,
            });
        }

        let start = Instant::now();

        // Run all health checks concurrently
        let (postgres_health, influx_health, redis_health, clickhouse_health) = tokio::join!(
            self.check_postgres(postgres),
            self.check_influx(influx),
            self.check_redis(redis),
            async {
                if let Some(ch) = clickhouse {
                    Some(self.check_clickhouse(ch).await)
                } else {
                    // None variant
                    None
                }
            }
        );

        let check_duration = start.elapsed();

        // Determine overall status
        let mut statuses = vec![
            postgres_health.status.clone(),
            influx_health.status.clone(),
            redis_health.status.clone(),
        ];

        if let Some(ref ch_health) = clickhouse_health {
            statuses.push(ch_health.status.clone());
        }

        let overall_status = self.determine_overall_status(&statuses);

        Ok(HealthStatus {
            overall_status,
            postgres: postgres_health,
            influx: influx_health,
            redis: redis_health,
            clickhouse: clickhouse_health,
            check_timestamp: chrono::Utc::now().timestamp() as u64,
            check_duration_ms: check_duration.as_millis() as u64,
        })
    }

    /// Check `PostgreSQL` health
    async fn check_postgres(&self, postgres: &PostgresPool) -> ComponentHealth {
        let start = Instant::now();

        let result = timeout(self.timeout_duration, postgres.health_check()).await;

        let latency = start.elapsed();

        match result {
            Ok(Ok(_)) => ComponentHealth {
                status: if latency.as_millis() > 100 {
                    SystemStatus::Degraded
                } else {
                    SystemStatus::Healthy
                },
                latency_ms: latency.as_millis() as f64,
                error_message: None,
                last_successful_check: Some(chrono::Utc::now().timestamp() as u64),
                consecutive_failures: 0,
            },
            Ok(Err(e)) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: latency.as_millis() as f64,
                error_message: Some(e.to_string()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
            Err(_) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: self.timeout_duration.as_millis() as f64,
                error_message: Some("Health check timeout".to_owned()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
        }
    }

    /// Check `InfluxDB` health
    async fn check_influx(&self, influx: &InfluxClient) -> ComponentHealth {
        let start = Instant::now();

        let result = timeout(self.timeout_duration, influx.health_check()).await;

        let latency = start.elapsed();

        match result {
            Ok(Ok(_)) => ComponentHealth {
                status: if latency.as_millis() > 200 {
                    SystemStatus::Degraded
                } else {
                    SystemStatus::Healthy
                },
                latency_ms: latency.as_millis() as f64,
                error_message: None,
                last_successful_check: Some(chrono::Utc::now().timestamp() as u64),
                consecutive_failures: 0,
            },
            Ok(Err(e)) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: latency.as_millis() as f64,
                error_message: Some(e.to_string()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
            Err(_) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: self.timeout_duration.as_millis() as f64,
                error_message: Some("Health check timeout".to_owned()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
        }
    }

    /// Check `Redis` health
    async fn check_redis(&self, redis: &RedisPool) -> ComponentHealth {
        let start = Instant::now();

        let result = timeout(self.timeout_duration, redis.health_check()).await;

        let latency = start.elapsed();

        match result {
            Ok(Ok(_)) => ComponentHealth {
                status: if latency.as_millis() > 50 {
                    SystemStatus::Degraded
                } else {
                    SystemStatus::Healthy
                },
                latency_ms: latency.as_millis() as f64,
                error_message: None,
                last_successful_check: Some(chrono::Utc::now().timestamp() as u64),
                consecutive_failures: 0,
            },
            Ok(Err(e)) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: latency.as_millis() as f64,
                error_message: Some(e.to_string()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
            Err(_) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: self.timeout_duration.as_millis() as f64,
                error_message: Some("Health check timeout".to_owned()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
        }
    }

    /// Check `ClickHouse` health
    async fn check_clickhouse(&self, clickhouse: &ClickHouseClient) -> ComponentHealth {
        let start = Instant::now();

        let result = timeout(self.timeout_duration, clickhouse.health_check()).await;

        let latency = start.elapsed();

        match result {
            Ok(Ok(_)) => ComponentHealth {
                status: if latency.as_millis() > 500 {
                    SystemStatus::Degraded
                } else {
                    SystemStatus::Healthy
                },
                latency_ms: latency.as_millis() as f64,
                error_message: None,
                last_successful_check: Some(chrono::Utc::now().timestamp() as u64),
                consecutive_failures: 0,
            },
            Ok(Err(e)) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: latency.as_millis() as f64,
                error_message: Some(e.to_string()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
            Err(_) => ComponentHealth {
                status: SystemStatus::Unhealthy,
                latency_ms: self.timeout_duration.as_millis() as f64,
                error_message: Some("Health check timeout".to_owned()),
                last_successful_check: None,
                consecutive_failures: 1,
            },
        }
    }

    /// Determine overall system status from component statuses
    fn determine_overall_status(&self, statuses: &[SystemStatus]) -> SystemStatus {
        if statuses.iter().all(|s| *s == SystemStatus::Healthy) {
            SystemStatus::Healthy
        } else if statuses.iter().any(|s| *s == SystemStatus::Unhealthy) {
            SystemStatus::Unhealthy
        } else if statuses.iter().any(|s| *s == SystemStatus::Degraded) {
            SystemStatus::Degraded
        } else {
            SystemStatus::Unknown
        }
    }
}

impl ComponentHealth {
    /// Create a health status for unknown/disabled components
    fn unknown() -> Self {
        Self {
            status: SystemStatus::Unknown,
            latency_ms: 0.0,
            error_message: Some("Health checking disabled".to_owned()),
            last_successful_check: None,
            consecutive_failures: 0,
        }
    }

    /// Check if this component is healthy enough for operations
    pub const fn is_operational(&self) -> bool {
        self.status.is_operational()
    }

    /// Get a human-readable status description
    pub fn status_description(&self) -> String {
        match &self.status {
            SystemStatus::Healthy => "Operating normally".to_owned(),
            SystemStatus::Degraded => format!(
                "Operating with degraded performance ({}ms latency)",
                self.latency_ms
            ),
            SystemStatus::Unhealthy => {
                if let Some(ref error) = self.error_message {
                    format!("System unhealthy: {}", error)
                } else {
                    "System unhealthy: Unknown error".to_owned()
                }
            },
            SystemStatus::Unknown => "Status unknown".to_owned(),
        }
    }
}

impl HealthStatus {
    /// Check if the overall system is operational
    pub const fn is_operational(&self) -> bool {
        self.overall_status.is_operational()
    }

    /// Get a summary of system health
    pub fn get_summary(&self) -> HealthSummary {
        let mut operational_components = 0;
        let mut total_components = 0;
        let mut max_latency: f64 = 0.0;

        // Check PostgreSQL
        total_components += 1;
        if self.postgres.is_operational() {
            operational_components += 1;
        }
        max_latency = max_latency.max(self.postgres.latency_ms);

        // Check InfluxDB
        total_components += 1;
        if self.influx.is_operational() {
            operational_components += 1;
        }
        max_latency = max_latency.max(self.influx.latency_ms);

        // Check Redis
        total_components += 1;
        if self.redis.is_operational() {
            operational_components += 1;
        }
        max_latency = max_latency.max(self.redis.latency_ms);

        // Check ClickHouse if present
        if let Some(ref ch) = self.clickhouse {
            total_components += 1;
            if ch.is_operational() {
                operational_components += 1;
            }
            max_latency = max_latency.max(ch.latency_ms);
        }

        HealthSummary {
            operational_components,
            total_components,
            operational_percentage: (operational_components as f64 / total_components as f64)
                * 100.0,
            max_latency_ms: max_latency,
            overall_status: self.overall_status.clone(),
        }
    }
}

/// Summary of health status across all components
#[derive(Debug, Clone, Serialize, Deserialize)]
/// HealthSummary
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct HealthSummary {
    /// Operational Components
    pub operational_components: u32,
    /// Total Components
    pub total_components: u32,
    /// Operational Percentage
    pub operational_percentage: f64,
    /// Max Latency Ms
    pub max_latency_ms: f64,
    /// Overall Status
    pub overall_status: SystemStatus,
}
