//! Test Environment Management
//!
//! Provides test environment types and lifecycle management for
//! containerized testing of taskservs, servers, and clusters.

// anyhow is only used in macros, not directly imported
use std::collections::HashMap;

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

/// Test environment type
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TestEnvironmentType {
    /// Single taskserv test
    SingleTaskserv,
    /// Full server simulation with multiple taskservs
    ServerSimulation,
    /// Multi-node cluster topology
    ClusterTopology,
}

/// Test environment status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TestEnvironmentStatus {
    Provisioning,
    Ready,
    Running,
    Failed,
    Completed,
    Cleaning,
    Destroyed,
}

/// Resource limits for containers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceLimits {
    /// CPU cores (1000 = 1 core)
    pub cpu_millicores: u32,
    /// Memory in MB
    pub memory_mb: u32,
    /// Disk in GB (optional)
    pub disk_gb: Option<u32>,
}

impl Default for ResourceLimits {
    fn default() -> Self {
        Self {
            cpu_millicores: 1000, // 1 core
            memory_mb: 2048,      // 2GB
            disk_gb: None,
        }
    }
}

/// Network configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
    /// Network name
    pub name: String,
    /// Subnet CIDR
    pub subnet: String,
    /// Enable DNS
    pub dns_enabled: bool,
    /// Custom DNS servers
    pub dns_servers: Vec<String>,
}

impl Default for NetworkConfig {
    fn default() -> Self {
        Self {
            name: format!("test-net-{}", &Uuid::new_v4().to_string()[..8]),
            subnet: "172.20.0.0/16".to_string(),
            dns_enabled: true,
            dns_servers: vec![],
        }
    }
}

/// Volume configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VolumeConfig {
    /// Volume name
    pub name: String,
    /// Mount path in container
    pub mount_path: String,
    /// Read-only mount
    pub read_only: bool,
}

/// Node configuration for cluster topologies
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
    /// Node name
    pub name: String,
    /// Node role (controlplane, worker, etcd-member, etc.)
    pub role: String,
    /// Taskservs to install on this node
    pub taskservs: Vec<String>,
    /// Resource limits
    pub resources: ResourceLimits,
    /// Environment variables
    pub environment: HashMap<String, String>,
}

/// Cluster topology configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterTopologyConfig {
    /// Cluster type (kubernetes, etcd, etc.)
    pub cluster_type: String,
    /// Nodes in the cluster
    pub nodes: Vec<NodeConfig>,
    /// Network configuration
    pub network: NetworkConfig,
    /// Shared volumes
    pub shared_volumes: Vec<VolumeConfig>,
}

/// Single taskserv test configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SingleTaskservConfig {
    /// Taskserv name
    pub taskserv: String,
    /// Base image
    pub base_image: String,
    /// Environment variables
    pub environment: HashMap<String, String>,
    /// Resource limits
    pub resources: ResourceLimits,
}

/// Server simulation configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerSimulationConfig {
    /// Server name
    pub server_name: String,
    /// Taskservs to install
    pub taskservs: Vec<String>,
    /// Base image
    pub base_image: String,
    /// Resolve dependencies automatically
    pub auto_resolve_dependencies: bool,
    /// Environment variables
    pub environment: HashMap<String, String>,
    /// Resource limits
    pub resources: ResourceLimits,
}

/// Test environment configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TestEnvironmentConfig {
    SingleTaskserv(SingleTaskservConfig),
    ServerSimulation(ServerSimulationConfig),
    ClusterTopology(ClusterTopologyConfig),
}

/// Container instance information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContainerInstance {
    /// Container ID
    pub container_id: String,
    /// Container name
    pub container_name: String,
    /// Node name (for topologies)
    pub node_name: Option<String>,
    /// IP address
    pub ip_address: Option<String>,
    /// Ports mapping
    pub ports: HashMap<u16, u16>,
    /// Status
    pub status: String,
}

/// Test environment metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestEnvironment {
    /// Environment ID
    pub id: String,
    /// Environment type
    pub env_type: TestEnvironmentType,
    /// Configuration
    pub config: TestEnvironmentConfig,
    /// Status
    pub status: TestEnvironmentStatus,
    /// Container instances
    pub containers: Vec<ContainerInstance>,
    /// Network ID
    pub network_id: Option<String>,
    /// Created timestamp
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// Started timestamp
    pub started_at: Option<chrono::DateTime<chrono::Utc>>,
    /// Completed timestamp
    pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
    /// Test results
    pub test_results: Vec<TestResult>,
    /// Logs
    pub logs: Vec<String>,
    /// Infrastructure context
    pub infra: Option<String>,
}

/// Test result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestResult {
    /// Test name
    pub name: String,
    /// Test status
    pub passed: bool,
    /// Test output
    pub output: String,
    /// Error message (if failed)
    pub error: Option<String>,
    /// Duration in milliseconds
    pub duration_ms: u64,
}

impl TestEnvironment {
    /// Create new test environment
    pub fn new(config: TestEnvironmentConfig, infra: Option<String>) -> Self {
        let env_type = match &config {
            TestEnvironmentConfig::SingleTaskserv(_) => TestEnvironmentType::SingleTaskserv,
            TestEnvironmentConfig::ServerSimulation(_) => TestEnvironmentType::ServerSimulation,
            TestEnvironmentConfig::ClusterTopology(_) => TestEnvironmentType::ClusterTopology,
        };

        Self {
            id: Uuid::new_v4().to_string(),
            env_type,
            config,
            status: TestEnvironmentStatus::Provisioning,
            containers: vec![],
            network_id: None,
            created_at: chrono::Utc::now(),
            started_at: None,
            completed_at: None,
            test_results: vec![],
            logs: vec![],
            infra,
        }
    }

    /// Add test result
    pub fn add_test_result(&mut self, result: TestResult) {
        self.test_results.push(result);
    }

    /// Add log entry
    pub fn add_log(&mut self, message: String) {
        let timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.3f");
        self.logs.push(format!("[{}] {}", timestamp, message));
    }

    /// Update status
    pub fn update_status(&mut self, status: TestEnvironmentStatus) {
        self.status = status.clone();
        match status {
            TestEnvironmentStatus::Running => {
                if self.started_at.is_none() {
                    self.started_at = Some(chrono::Utc::now());
                }
            }
            TestEnvironmentStatus::Completed
            | TestEnvironmentStatus::Failed
            | TestEnvironmentStatus::Destroyed => {
                if self.completed_at.is_none() {
                    self.completed_at = Some(chrono::Utc::now());
                }
            }
            _ => {}
        }
    }

    /// Check if environment is active
    pub fn is_active(&self) -> bool {
        matches!(
            self.status,
            TestEnvironmentStatus::Provisioning
                | TestEnvironmentStatus::Ready
                | TestEnvironmentStatus::Running
        )
    }

    /// Get environment summary
    pub fn summary(&self) -> String {
        let duration = if let Some(completed) = self.completed_at {
            let duration = completed - self.created_at;
            format!("{}s", duration.num_seconds())
        } else {
            "running".to_string()
        };

        let passed_tests = self.test_results.iter().filter(|r| r.passed).count();
        let total_tests = self.test_results.len();

        format!(
            "Environment {} ({:?}): {:?} - {} containers, {}/{} tests passed, duration: {}",
            self.id,
            self.env_type,
            self.status,
            self.containers.len(),
            passed_tests,
            total_tests,
            duration
        )
    }
}

/// Test environment creation request
#[derive(Debug, Clone, Deserialize)]
pub struct CreateTestEnvironmentRequest {
    /// Configuration
    pub config: TestEnvironmentConfig,
    /// Infrastructure context
    pub infra: Option<String>,
    /// Auto-start tests
    pub auto_start: bool,
    /// Auto-cleanup after completion
    pub auto_cleanup: bool,
}

/// Test execution request
#[derive(Debug, Clone, Deserialize)]
pub struct RunTestRequest {
    /// Tests to run (empty = all)
    pub tests: Vec<String>,
    /// Timeout in seconds
    pub timeout_seconds: Option<u64>,
}

/// Test environment response
#[derive(Debug, Clone, Serialize)]
pub struct TestEnvironmentResponse {
    pub success: bool,
    pub environment_id: Option<String>,
    pub message: String,
}
