//! Model Deployment and Versioning System
//!
//! This module provides a comprehensive model deployment system with semantic versioning,
//! A/B testing, hot-swapping, validation pipelines, and automatic rollback capabilities.
//!
//! ## Key Features
//!
//! - **Semantic Versioning**: Full semantic versioning support with compatibility checking
//! - **Atomic Hot-Swapping**: Zero-downtime model updates using compare-and-swap operations
//! - **A/B Testing**: Statistical traffic splitting with significance testing
//! - **Validation Pipeline**: Multi-stage validation before production deployment
//! - **Performance Monitoring**: Real-time metrics with automatic rollback triggers
//! - **Production Safety**: Circuit breakers, fallback mechanisms, and disaster recovery

use std::collections::HashMap;
use std::time::{Duration, SystemTime};

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

use crate::ModelType;

pub mod versioning;
pub mod ab_testing;

// Re-export commonly used types for convenience
pub use versioning::ModelVersion;
pub use ab_testing::ABTestConfig;

/// Model deployment status
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeploymentStatus {
    /// Model is loading into memory
    Loading,
    /// Model is validating
    Validating,
    /// Model is in canary deployment (limited traffic)
    Canary,
    /// Model is active and serving production traffic
    Active,
    /// Model is deprecated but still serving
    Deprecated,
    /// Model has been retired and is no longer serving
    Retired,
    /// Model deployment failed
    Failed,
}

/// Model lifecycle state
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ModelLifecycle {
    /// Model is in development
    Development,
    /// Model is in testing phase
    Testing,
    /// Model is in staging environment
    Staging,
    /// Model is in production
    Production,
    /// Model is archived
    Archived,
}

/// Model deployment metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentMetadata {
    /// Unique deployment ID
    pub deployment_id: Uuid,
    /// Model identifier
    pub model_id: String,
    /// Model type
    pub model_type: ModelType,
    /// Model version
    pub version: ModelVersion,
    /// Deployment status
    pub status: DeploymentStatus,
    /// Lifecycle stage
    pub lifecycle: ModelLifecycle,
    /// Deployment timestamp
    pub deployed_at: SystemTime,
    /// Last updated timestamp
    pub updated_at: SystemTime,
    /// Deployer information
    pub deployed_by: String,
    /// Environment (dev, staging, prod)
    pub environment: String,
    /// Resource requirements
    pub resource_requirements: ResourceRequirements,
    /// Performance baseline
    pub performance_baseline: PerformanceBaseline,
    /// Configuration checksum
    pub config_checksum: String,
    /// Additional tags
    pub tags: HashMap<String, String>,
}

/// Resource requirements for model deployment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceRequirements {
    /// Memory requirement in MB
    pub memory_mb: u64,
    /// CPU cores required
    pub cpu_cores: f32,
    /// `GPU` memory requirement in MB (if applicable)
    pub gpu_memory_mb: Option<u64>,
    /// Maximum latency tolerance in microseconds
    pub max_latency_us: u64,
    /// Minimum throughput requirement (predictions per second)
    pub min_throughput_pps: u32,
}

/// Performance baseline for comparison
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceBaseline {
    /// Average inference latency in microseconds
    pub avg_latency_us: f64,
    /// 95th percentile latency in microseconds
    pub p95_latency_us: f64,
    /// 99th percentile latency in microseconds
    pub p99_latency_us: f64,
    /// Throughput in predictions per second
    pub throughput_pps: f64,
    /// Memory usage in MB
    pub memory_usage_mb: f64,
    /// Accuracy score (0.0 to 1.0)
    pub accuracy_score: f64,
    /// Error rate (0.0 to 1.0)
    pub error_rate: f64,
}

impl Default for PerformanceBaseline {
    fn default() -> Self {
        Self {
            avg_latency_us: 0.0,
            p95_latency_us: 0.0,
            p99_latency_us: 0.0,
            throughput_pps: 0.0,
            memory_usage_mb: 0.0,
            accuracy_score: 0.0,
            error_rate: 0.0,
        }
    }
}

/// Deployment configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentConfig {
    /// Enable A/B testing
    pub enable_ab_testing: bool,
    /// A/B test configuration
    pub ab_test_config: Option<ABTestConfig>,
    /// Rollback configuration
    pub rollback_config: RollbackConfig,
    /// Canary deployment percentage (0.0 to 1.0)
    pub canary_percentage: f32,
    /// Maximum deployment time before timeout
    pub deployment_timeout: Duration,
    /// Health check interval
    pub health_check_interval: Duration,
}

impl Default for DeploymentConfig {
    fn default() -> Self {
        Self {
            enable_ab_testing: false,
            ab_test_config: None,
            rollback_config: RollbackConfig::default(),
            canary_percentage: 0.05, // 5% canary traffic
            deployment_timeout: Duration::from_secs(300), // 5 minutes
            health_check_interval: Duration::from_secs(30),
        }
    }
}

/// Rollback configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RollbackConfig {
    /// Enable automatic rollback
    pub auto_rollback_enabled: bool,
    /// Error rate threshold for rollback (0.0 to 1.0)
    pub error_rate_threshold: f32,
    /// Latency degradation threshold for rollback (multiplier)
    pub latency_degradation_threshold: f32,
    /// Minimum samples before triggering rollback
    pub min_samples_for_rollback: u32,
    /// Rollback timeout
    pub rollback_timeout: Duration,
}

impl Default for RollbackConfig {
    fn default() -> Self {
        Self {
            auto_rollback_enabled: true,
            error_rate_threshold: 0.05, // 5% error rate
            latency_degradation_threshold: 1.5, // 50% latency increase
            min_samples_for_rollback: 100,
            rollback_timeout: Duration::from_secs(60),
        }
    }
}

/// Deployment event for auditing
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentEvent {
    /// Event ID
    pub event_id: Uuid,
    /// Deployment ID
    pub deployment_id: Uuid,
    /// Event type
    pub event_type: DeploymentEventType,
    /// Event timestamp
    pub timestamp: SystemTime,
    /// Event message
    pub message: String,
    /// Additional metadata
    pub metadata: HashMap<String, String>,
}

/// Types of deployment events
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeploymentEventType {
    /// Deployment started
    DeploymentStarted,
    /// Validation passed
    ValidationPassed,
    /// Validation failed
    ValidationFailed,
    /// Canary deployment started
    CanaryStarted,
    /// Model activated
    ModelActivated,
    /// A/B test started
    ABTestStarted,
    /// A/B test completed
    ABTestCompleted,
    /// Rollback triggered
    RollbackTriggered,
    /// Rollback completed
    RollbackCompleted,
    /// Model retired
    ModelRetired,
    /// Health check failed
    HealthCheckFailed,
    /// Performance degradation detected
    PerformanceDegradation,
}

/// Result of a deployment operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentResult {
    /// Success flag
    pub success: bool,
    /// Deployment ID (if successful)
    pub deployment_id: Option<Uuid>,
    /// Error message (if failed)
    pub error_message: Option<String>,
    /// Deployment metadata
    pub metadata: Option<DeploymentMetadata>,
    /// Duration of deployment
    pub deployment_duration: Duration,
    /// Events generated during deployment
    pub events: Vec<DeploymentEvent>,
}

impl DeploymentResult {
    /// Create successful deployment result
    pub fn success(
        deployment_id: Uuid,
        metadata: DeploymentMetadata,
        duration: Duration,
    ) -> Self {
        Self {
            success: true,
            deployment_id: Some(deployment_id),
            error_message: None,
            metadata: Some(metadata),
            deployment_duration: duration,
            events: Vec::new(),
        }
    }

    /// Create failed deployment result
    pub fn failure(error: String, duration: Duration) -> Self {
        Self {
            success: false,
            deployment_id: None,
            error_message: Some(error),
            metadata: None,
            deployment_duration: duration,
            events: Vec::new(),
        }
    }

    /// Add event
    pub fn add_event(&mut self, event: DeploymentEvent) {
        self.events.push(event);
    }
}

/// Create deployment metadata with defaults
pub fn create_deployment_metadata(
    model_id: String,
    model_type: ModelType,
    version: ModelVersion,
    deployed_by: String,
    environment: String,
) -> DeploymentMetadata {
    let now = SystemTime::now();
    
    DeploymentMetadata {
        deployment_id: Uuid::new_v4(),
        model_id,
        model_type,
        version,
        status: DeploymentStatus::Loading,
        lifecycle: ModelLifecycle::Development,
        deployed_at: now,
        updated_at: now,
        deployed_by,
        environment,
        resource_requirements: ResourceRequirements {
            memory_mb: 512,
            cpu_cores: 1.0,
            gpu_memory_mb: None,
            max_latency_us: 1000,
            min_throughput_pps: 1000,
        },
        performance_baseline: PerformanceBaseline::default(),
        config_checksum: "".to_owned(),
        tags: HashMap::new(),
    }
}

/// Create deployment event
pub fn create_deployment_event(
    deployment_id: Uuid,
    event_type: DeploymentEventType,
    message: String,
) -> DeploymentEvent {
    DeploymentEvent {
        event_id: Uuid::new_v4(),
        deployment_id,
        event_type,
        timestamp: SystemTime::now(),
        message,
        metadata: HashMap::new(),
    }
}

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

    #[test]
    fn test_deployment_metadata_creation() {
        let metadata = create_deployment_metadata(
            "test_model".to_owned(),
            ModelType::DQN,
            ModelVersion::new(1, 0, 0),
            "test_user".to_owned(),
            "test".to_owned(),
        );

        assert_eq!(metadata.model_id, "test_model");
        assert_eq!(metadata.model_type, ModelType::DQN);
        assert_eq!(metadata.version.major, 1);
        assert_eq!(metadata.status, DeploymentStatus::Loading);
    }

    #[test]
    fn test_deployment_result_success() {
        let deployment_id = Uuid::new_v4();
        let metadata = create_deployment_metadata(
            "test".to_owned(),
            ModelType::DQN,
            ModelVersion::new(1, 0, 0),
            "user".to_owned(),
            "test".to_owned(),
        );
        
        let result = DeploymentResult::success(
            deployment_id,
            metadata,
            Duration::from_secs(30),
        );

        assert!(result.success);
        assert_eq!(result.deployment_id, Some(deployment_id));
        assert!(result.error_message.is_none());
    }

    #[test]
    fn test_deployment_result_failure() {
        let result = DeploymentResult::failure(
            "Test error".to_owned(),
            Duration::from_secs(10),
        );

        assert!(!result.success);
        assert!(result.deployment_id.is_none());
        assert_eq!(result.error_message, Some("Test error".to_owned()));
    }

    #[test]
    fn test_deployment_event_creation() {
        let deployment_id = Uuid::new_v4();
        let event = create_deployment_event(
            deployment_id,
            DeploymentEventType::DeploymentStarted,
            "Deployment started".to_owned(),
        );

        assert_eq!(event.deployment_id, deployment_id);
        assert_eq!(event.event_type, DeploymentEventType::DeploymentStarted);
        assert_eq!(event.message, "Deployment started");
    }
}
