//! Health check types

use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::PoolId;

/// Unique identifier for a health check
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct HealthCheckId(Uuid);

impl HealthCheckId {
    /// Create a new random HealthCheckId
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    /// Create from existing UUID
    pub fn from_uuid(uuid: Uuid) -> Self {
        Self(uuid)
    }

    /// Get the inner UUID
    pub fn as_uuid(&self) -> &Uuid {
        &self.0
    }
}

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

impl std::fmt::Display for HealthCheckId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Health check type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum HealthCheckType {
    /// TCP connection check
    #[default]
    Tcp,
    /// HTTP GET request
    Http,
    /// HTTPS GET request
    Https,
    /// UDP probe
    Udp,
    /// Ping (ICMP)
    Ping,
}


/// HTTP health check configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpHealthConfig {
    /// HTTP method (GET, HEAD)
    pub method: String,
    /// URL path to check
    pub path: String,
    /// Expected status codes (e.g., [200, 201, 204])
    pub expected_codes: Vec<u16>,
    /// Host header
    pub host: Option<String>,
}

impl Default for HttpHealthConfig {
    fn default() -> Self {
        Self {
            method: "GET".to_string(),
            path: "/health".to_string(),
            expected_codes: vec![200],
            host: None,
        }
    }
}

/// Health check configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheck {
    /// Unique identifier
    pub id: HealthCheckId,
    /// Human-readable name
    pub name: String,
    /// Parent pool
    pub pool_id: PoolId,
    /// Check type
    pub check_type: HealthCheckType,
    /// Interval between checks (seconds)
    pub interval_seconds: u32,
    /// Timeout for each check (seconds)
    pub timeout_seconds: u32,
    /// Number of successful checks to mark healthy
    pub healthy_threshold: u32,
    /// Number of failed checks to mark unhealthy
    pub unhealthy_threshold: u32,
    /// HTTP-specific configuration
    pub http_config: Option<HttpHealthConfig>,
    /// Enabled state
    pub enabled: bool,
    /// Creation timestamp
    pub created_at: u64,
    /// Last update timestamp
    pub updated_at: u64,
}

impl HealthCheck {
    /// Create a new TCP health check with defaults
    pub fn new_tcp(name: impl Into<String>, pool_id: PoolId) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();
        
        Self {
            id: HealthCheckId::new(),
            name: name.into(),
            pool_id,
            check_type: HealthCheckType::Tcp,
            interval_seconds: 30,
            timeout_seconds: 10,
            healthy_threshold: 2,
            unhealthy_threshold: 3,
            http_config: None,
            enabled: true,
            created_at: now,
            updated_at: now,
        }
    }

    /// Create a new HTTP health check with defaults
    pub fn new_http(name: impl Into<String>, pool_id: PoolId, path: impl Into<String>) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();
        
        Self {
            id: HealthCheckId::new(),
            name: name.into(),
            pool_id,
            check_type: HealthCheckType::Http,
            interval_seconds: 30,
            timeout_seconds: 10,
            healthy_threshold: 2,
            unhealthy_threshold: 3,
            http_config: Some(HttpHealthConfig {
                method: "GET".to_string(),
                path: path.into(),
                expected_codes: vec![200],
                host: None,
            }),
            enabled: true,
            created_at: now,
            updated_at: now,
        }
    }
}

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

    #[test]
    fn test_tcp_health_check() {
        let pool_id = PoolId::new();
        let hc = HealthCheck::new_tcp("tcp-check", pool_id);
        assert_eq!(hc.check_type, HealthCheckType::Tcp);
        assert_eq!(hc.interval_seconds, 30);
        assert!(hc.http_config.is_none());
    }

    #[test]
    fn test_http_health_check() {
        let pool_id = PoolId::new();
        let hc = HealthCheck::new_http("http-check", pool_id, "/healthz");
        assert_eq!(hc.check_type, HealthCheckType::Http);
        assert!(hc.http_config.is_some());
        assert_eq!(hc.http_config.as_ref().unwrap().path, "/healthz");
    }
}
