//! Comprehensive tests for traits module
//!
//! Tests cover:
//! - HealthStatus and DetailedHealth structures
//! - RateLimitStatus structure
//! - Trait implementations with mock types
//! - Serialization/deserialization

use chrono::Utc;
use common::traits::{DetailedHealth, HealthStatus, RateLimitStatus};
use common::types::ServiceStatus;
use std::collections::HashMap;

// ============================================================================
// HealthStatus Tests
// ============================================================================

#[test]
fn test_health_status_creation() {
    let timestamp = Utc::now();
    let status = HealthStatus {
        status: ServiceStatus::Running,
        timestamp,
        message: Some("All systems operational".to_string()),
    };

    assert_eq!(status.status, ServiceStatus::Running);
    assert_eq!(status.timestamp, timestamp);
    assert_eq!(status.message, Some("All systems operational".to_string()));
}

#[test]
fn test_health_status_without_message() {
    let timestamp = Utc::now();
    let status = HealthStatus {
        status: ServiceStatus::Running,
        timestamp,
        message: None,
    };

    assert_eq!(status.status, ServiceStatus::Running);
    assert!(status.message.is_none());
}

#[test]
fn test_health_status_degraded() {
    let status = HealthStatus {
        status: ServiceStatus::Degraded,
        timestamp: Utc::now(),
        message: Some("Performance degraded".to_string()),
    };

    assert_eq!(status.status, ServiceStatus::Degraded);
    assert!(status.message.is_some());
}

#[test]
fn test_health_status_unhealthy() {
    let status = HealthStatus {
        status: ServiceStatus::Stopped,
        timestamp: Utc::now(),
        message: Some("Service unavailable".to_string()),
    };

    assert_eq!(status.status, ServiceStatus::Stopped);
}

#[test]
fn test_health_status_serialization() {
    let status = HealthStatus {
        status: ServiceStatus::Running,
        timestamp: Utc::now(),
        message: Some("OK".to_string()),
    };

    let json = serde_json::to_string(&status).expect("Failed to serialize");
    let deserialized: HealthStatus = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.status, status.status);
    assert_eq!(deserialized.message, status.message);
}

#[test]
fn test_health_status_clone() {
    let status = HealthStatus {
        status: ServiceStatus::Running,
        timestamp: Utc::now(),
        message: Some("Test".to_string()),
    };

    let cloned = status.clone();
    assert_eq!(cloned.status, status.status);
    assert_eq!(cloned.message, status.message);
}

// ============================================================================
// DetailedHealth Tests
// ============================================================================

#[test]
fn test_detailed_health_creation() {
    let timestamp = Utc::now();
    let base_status = HealthStatus {
        status: ServiceStatus::Running,
        timestamp,
        message: None,
    };

    let mut metrics = HashMap::new();
    metrics.insert("cpu_usage".to_string(), 45.5);
    metrics.insert("memory_usage".to_string(), 60.2);

    let mut components = HashMap::new();
    components.insert(
        "database".to_string(),
        HealthStatus {
            status: ServiceStatus::Running,
            timestamp,
            message: Some("DB OK".to_string()),
        },
    );

    let detailed = DetailedHealth {
        status: base_status.clone(),
        metrics: metrics.clone(),
        components: components.clone(),
    };

    assert_eq!(detailed.status.status, ServiceStatus::Running);
    assert_eq!(detailed.metrics.len(), 2);
    assert_eq!(detailed.components.len(), 1);
    assert_eq!(detailed.metrics.get("cpu_usage"), Some(&45.5));
}

#[test]
fn test_detailed_health_empty_metrics() {
    let detailed = DetailedHealth {
        status: HealthStatus {
            status: ServiceStatus::Running,
            timestamp: Utc::now(),
            message: None,
        },
        metrics: HashMap::new(),
        components: HashMap::new(),
    };

    assert!(detailed.metrics.is_empty());
    assert!(detailed.components.is_empty());
}

#[test]
fn test_detailed_health_multiple_components() {
    let timestamp = Utc::now();
    let mut components = HashMap::new();

    components.insert(
        "database".to_string(),
        HealthStatus {
            status: ServiceStatus::Running,
            timestamp,
            message: Some("DB OK".to_string()),
        },
    );

    components.insert(
        "cache".to_string(),
        HealthStatus {
            status: ServiceStatus::Degraded,
            timestamp,
            message: Some("Cache slow".to_string()),
        },
    );

    components.insert(
        "queue".to_string(),
        HealthStatus {
            status: ServiceStatus::Running,
            timestamp,
            message: Some("Queue OK".to_string()),
        },
    );

    let detailed = DetailedHealth {
        status: HealthStatus {
            status: ServiceStatus::Degraded, // Overall status reflects degraded component
            timestamp,
            message: Some("Some components degraded".to_string()),
        },
        metrics: HashMap::new(),
        components,
    };

    assert_eq!(detailed.components.len(), 3);
    assert_eq!(detailed.status.status, ServiceStatus::Degraded);

    let cache_status = detailed.components.get("cache").unwrap();
    assert_eq!(cache_status.status, ServiceStatus::Degraded);
}

#[test]
fn test_detailed_health_serialization() {
    let mut metrics = HashMap::new();
    metrics.insert("latency_ms".to_string(), 12.5);

    let detailed = DetailedHealth {
        status: HealthStatus {
            status: ServiceStatus::Running,
            timestamp: Utc::now(),
            message: None,
        },
        metrics,
        components: HashMap::new(),
    };

    let json = serde_json::to_string(&detailed).expect("Failed to serialize");
    let deserialized: DetailedHealth = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.status.status, ServiceStatus::Running);
    assert_eq!(deserialized.metrics.len(), 1);
    assert_eq!(deserialized.metrics.get("latency_ms"), Some(&12.5));
}

#[test]
fn test_detailed_health_clone() {
    let mut metrics = HashMap::new();
    metrics.insert("test_metric".to_string(), 100.0);

    let detailed = DetailedHealth {
        status: HealthStatus {
            status: ServiceStatus::Running,
            timestamp: Utc::now(),
            message: None,
        },
        metrics,
        components: HashMap::new(),
    };

    let cloned = detailed.clone();
    assert_eq!(cloned.metrics.len(), detailed.metrics.len());
    assert_eq!(cloned.status.status, detailed.status.status);
}

// ============================================================================
// RateLimitStatus Tests
// ============================================================================

#[test]
fn test_rate_limit_status_creation() {
    let status = RateLimitStatus {
        current_count: 50,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 45,
    };

    assert_eq!(status.current_count, 50);
    assert_eq!(status.max_requests, 100);
    assert_eq!(status.window_seconds, 60);
    assert_eq!(status.reset_in_seconds, 45);
}

#[test]
fn test_rate_limit_status_at_limit() {
    let status = RateLimitStatus {
        current_count: 100,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 30,
    };

    assert_eq!(status.current_count, status.max_requests);
}

#[test]
fn test_rate_limit_status_under_limit() {
    let status = RateLimitStatus {
        current_count: 25,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 55,
    };

    assert!(status.current_count < status.max_requests);
}

#[test]
fn test_rate_limit_status_zero_count() {
    let status = RateLimitStatus {
        current_count: 0,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 60,
    };

    assert_eq!(status.current_count, 0);
}

#[test]
fn test_rate_limit_status_about_to_reset() {
    let status = RateLimitStatus {
        current_count: 90,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 1, // About to reset
    };

    assert_eq!(status.reset_in_seconds, 1);
}

#[test]
fn test_rate_limit_status_serialization() {
    let status = RateLimitStatus {
        current_count: 75,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 30,
    };

    let json = serde_json::to_string(&status).expect("Failed to serialize");
    let deserialized: RateLimitStatus = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.current_count, status.current_count);
    assert_eq!(deserialized.max_requests, status.max_requests);
    assert_eq!(deserialized.window_seconds, status.window_seconds);
    assert_eq!(deserialized.reset_in_seconds, status.reset_in_seconds);
}

#[test]
fn test_rate_limit_status_clone() {
    let status = RateLimitStatus {
        current_count: 50,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 45,
    };

    let cloned = status.clone();
    assert_eq!(cloned.current_count, status.current_count);
    assert_eq!(cloned.max_requests, status.max_requests);
}

#[test]
fn test_rate_limit_status_utilization_calculation() {
    let status = RateLimitStatus {
        current_count: 75,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 30,
    };

    // Calculate utilization percentage
    let utilization = (status.current_count as f64 / status.max_requests as f64) * 100.0;
    assert_eq!(utilization, 75.0);
}

#[test]
fn test_rate_limit_status_remaining_requests() {
    let status = RateLimitStatus {
        current_count: 40,
        max_requests: 100,
        window_seconds: 60,
        reset_in_seconds: 20,
    };

    let remaining = status.max_requests - status.current_count;
    assert_eq!(remaining, 60);
}

// ============================================================================
// Mock Trait Implementations for Testing
// ============================================================================

// ============================================================================
// Trait Default Implementation Tests
// ============================================================================

#[test]
fn test_reloadable_default_supports_reload() {
    use async_trait::async_trait;
    use common::traits::Reloadable;

    struct TestReloadable;

    #[async_trait]
    impl Reloadable for TestReloadable {
        async fn reload(&mut self) -> common::error::CommonResult<()> {
            Ok(())
        }
        // uses default supports_reload implementation
    }

    let component = TestReloadable;
    assert!(component.supports_reload());
}

#[test]
fn test_graceful_shutdown_default_timeout() {
    use async_trait::async_trait;
    use common::traits::GracefulShutdown;

    struct TestShutdown;

    #[async_trait]
    impl GracefulShutdown for TestShutdown {
        async fn shutdown(&mut self) -> common::error::CommonResult<()> {
            Ok(())
        }

        async fn force_shutdown(&mut self) -> common::error::CommonResult<()> {
            Ok(())
        }
        // uses default shutdown_timeout_seconds implementation
    }

    let component = TestShutdown;
    assert_eq!(component.shutdown_timeout_seconds(), 30);
}

#[cfg(test)]
mod mock_implementations {
    use super::*;
    use async_trait::async_trait;
    use common::error::CommonResult;
    use common::traits::{CircuitBreaker, Configurable, HealthCheck, RateLimited};

    #[derive(Clone)]
    struct MockConfig {
        value: String,
    }

    struct MockComponent {
        config: MockConfig,
        is_healthy: bool,
        circuit_open: bool,
        failure_count: u64,
        request_count: u64,
    }

    #[async_trait]
    impl Configurable for MockComponent {
        type Config = MockConfig;

        async fn configure(&mut self, config: Self::Config) -> CommonResult<()> {
            self.config = config;
            Ok(())
        }

        fn get_config(&self) -> &Self::Config {
            &self.config
        }

        fn validate_config(_config: &Self::Config) -> CommonResult<()> {
            Ok(())
        }
    }

    #[async_trait]
    impl HealthCheck for MockComponent {
        async fn health_check(&self) -> CommonResult<HealthStatus> {
            Ok(HealthStatus {
                status: if self.is_healthy {
                    ServiceStatus::Running
                } else {
                    ServiceStatus::Stopped
                },
                timestamp: Utc::now(),
                message: None,
            })
        }

        async fn detailed_health(&self) -> CommonResult<DetailedHealth> {
            let mut metrics = HashMap::new();
            metrics.insert("request_count".to_string(), self.request_count as f64);

            Ok(DetailedHealth {
                status: self.health_check().await?,
                metrics,
                components: HashMap::new(),
            })
        }
    }

    impl CircuitBreaker for MockComponent {
        fn is_circuit_open(&self) -> bool {
            self.circuit_open
        }

        fn failure_count(&self) -> u64 {
            self.failure_count
        }

        fn reset_circuit(&mut self) {
            self.circuit_open = false;
            self.failure_count = 0;
        }
    }

    impl RateLimited for MockComponent {
        fn is_allowed(&self) -> bool {
            self.request_count < 100
        }

        fn rate_limit_status(&self) -> RateLimitStatus {
            RateLimitStatus {
                current_count: self.request_count,
                max_requests: 100,
                window_seconds: 60,
                reset_in_seconds: 30,
            }
        }
    }

    #[tokio::test]
    async fn test_mock_configurable() {
        let mut component = MockComponent {
            config: MockConfig {
                value: "initial".to_string(),
            },
            is_healthy: true,
            circuit_open: false,
            failure_count: 0,
            request_count: 0,
        };

        let new_config = MockConfig {
            value: "updated".to_string(),
        };

        component.configure(new_config.clone()).await.unwrap();
        assert_eq!(component.get_config().value, "updated");
    }

    #[tokio::test]
    async fn test_mock_health_check() {
        let component = MockComponent {
            config: MockConfig {
                value: "test".to_string(),
            },
            is_healthy: true,
            circuit_open: false,
            failure_count: 0,
            request_count: 0,
        };

        let health = component.health_check().await.unwrap();
        assert_eq!(health.status, ServiceStatus::Running);
    }

    #[tokio::test]
    async fn test_mock_detailed_health() {
        let component = MockComponent {
            config: MockConfig {
                value: "test".to_string(),
            },
            is_healthy: true,
            circuit_open: false,
            failure_count: 0,
            request_count: 42,
        };

        let detailed = component.detailed_health().await.unwrap();
        assert_eq!(detailed.status.status, ServiceStatus::Running);
        assert_eq!(detailed.metrics.get("request_count"), Some(&42.0));
    }

    #[test]
    fn test_mock_circuit_breaker() {
        let mut component = MockComponent {
            config: MockConfig {
                value: "test".to_string(),
            },
            is_healthy: true,
            circuit_open: true,
            failure_count: 5,
            request_count: 0,
        };

        assert!(component.is_circuit_open());
        assert_eq!(component.failure_count(), 5);

        component.reset_circuit();
        assert!(!component.is_circuit_open());
        assert_eq!(component.failure_count(), 0);
    }

    #[test]
    fn test_mock_rate_limited() {
        let component = MockComponent {
            config: MockConfig {
                value: "test".to_string(),
            },
            is_healthy: true,
            circuit_open: false,
            failure_count: 0,
            request_count: 50,
        };

        assert!(component.is_allowed());

        let status = component.rate_limit_status();
        assert_eq!(status.current_count, 50);
        assert_eq!(status.max_requests, 100);
    }

    #[test]
    fn test_mock_rate_limited_at_limit() {
        let component = MockComponent {
            config: MockConfig {
                value: "test".to_string(),
            },
            is_healthy: true,
            circuit_open: false,
            failure_count: 0,
            request_count: 100,
        };

        assert!(!component.is_allowed());
    }
}
