//! Settings Tools for Installer Configuration Management
//!
//! Provides MCP tools for intelligent installer configuration:
//! - Query complete/partial settings
//! - Fill missing configuration values
//! - Validate complete configurations
//! - Get mode-specific defaults
//! - AI-powered platform and service recommendations

use std::collections::HashMap;

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};

use crate::errors::ProvisioningError;

/// Platform types for deployment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Platform {
    Docker,
    Podman,
    Kubernetes,
    OrbStack,
}

impl Platform {
    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "docker" => Some(Self::Docker),
            "podman" => Some(Self::Podman),
            "kubernetes" | "k8s" => Some(Self::Kubernetes),
            "orbstack" => Some(Self::OrbStack),
            _ => None,
        }
    }

    pub fn to_str(&self) -> &'static str {
        match self {
            Self::Docker => "docker",
            Self::Podman => "podman",
            Self::Kubernetes => "kubernetes",
            Self::OrbStack => "orbstack",
        }
    }
}

/// Deployment modes with resource requirements
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::upper_case_acronyms)]
pub enum DeploymentMode {
    Solo,
    MultiUser,
    CICD,
    Enterprise,
}

impl DeploymentMode {
    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "solo" | "development" | "dev" => Some(Self::Solo),
            "multiuser" | "multi-user" | "team" => Some(Self::MultiUser),
            "cicd" | "ci/cd" | "ci-cd" | "automation" => Some(Self::CICD),
            "enterprise" | "production" | "prod" => Some(Self::Enterprise),
            _ => None,
        }
    }

    pub fn to_str(&self) -> &'static str {
        match self {
            Self::Solo => "solo",
            Self::MultiUser => "multiuser",
            Self::CICD => "cicd",
            Self::Enterprise => "enterprise",
        }
    }

    pub fn description(&self) -> &'static str {
        match self {
            Self::Solo => "Development - Single user",
            Self::MultiUser => "Team - Collaboration with Git",
            Self::CICD => "Automation - CI/CD pipelines",
            Self::Enterprise => "Production - Full observability",
        }
    }

    pub fn service_count(&self) -> &'static str {
        match self {
            Self::Solo => "5 services",
            Self::MultiUser => "7 services",
            Self::CICD => "8-10 services",
            Self::Enterprise => "15+ services",
        }
    }

    pub fn min_cpu_cores(&self) -> usize {
        match self {
            Self::Solo => 2,
            Self::MultiUser => 4,
            Self::CICD => 8,
            Self::Enterprise => 16,
        }
    }

    pub fn min_memory_gb(&self) -> f64 {
        match self {
            Self::Solo => 4.0,
            Self::MultiUser => 8.0,
            Self::CICD => 16.0,
            Self::Enterprise => 32.0,
        }
    }

    pub fn recommended_services(&self) -> Vec<&'static str> {
        match self {
            Self::Solo => vec![
                "orchestrator",
                "control-center",
                "coredns",
                "mcp-server",
                "api-gateway",
            ],
            Self::MultiUser => vec![
                "orchestrator",
                "control-center",
                "coredns",
                "gitea",
                "postgres",
                "oci-registry",
                "mcp-server",
            ],
            Self::CICD => vec![
                "orchestrator",
                "control-center",
                "coredns",
                "gitea",
                "postgres",
                "api-server",
                "oci-registry",
                "mcp-server",
            ],
            Self::Enterprise => vec![
                "orchestrator",
                "control-center",
                "coredns",
                "gitea",
                "postgres",
                "api-server",
                "harbor",
                "kms",
                "prometheus",
                "grafana",
                "loki",
                "nginx",
                "mcp-server",
                "api-gateway",
            ],
        }
    }
}

/// Service configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceConfig {
    pub name: String,
    pub description: String,
    pub port: u16,
    pub enabled: bool,
    pub required: bool,
}

/// Complete installer configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstallerConfig {
    pub platform: Option<Platform>,
    pub mode: Option<DeploymentMode>,
    pub domain: Option<String>,
    pub services: Vec<ServiceConfig>,
    pub auto_generate_secrets: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub custom_settings: Option<HashMap<String, Value>>,
}

impl Default for InstallerConfig {
    fn default() -> Self {
        Self {
            platform: None,
            mode: None,
            domain: Some("localhost".to_string()),
            services: Vec::new(),
            auto_generate_secrets: true,
            custom_settings: None,
        }
    }
}

/// Platform detection result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformDetection {
    pub platform: Platform,
    pub available: bool,
    pub version: String,
    pub recommended: bool,
}

/// AI recommendation for configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigRecommendation {
    pub field: String,
    pub recommended_value: Value,
    pub reason: String,
    pub confidence: f32,
}

/// Settings Tools implementation
pub struct SettingsTools {
    /// Platform detection cache
    detected_platforms: Option<Vec<PlatformDetection>>,
}

impl SettingsTools {
    pub fn new() -> Self {
        Self {
            detected_platforms: None,
        }
    }

    /// Get installer settings (complete or partial query)
    pub async fn get_settings(&mut self, query: Option<&str>) -> Result<Value> {
        let _config = InstallerConfig::default();

        if let Some(q) = query {
            // Parse query to return specific settings
            if q.contains("platform") {
                Ok(json!({
                    "platforms": self.detect_platforms().await?,
                }))
            } else if q.contains("mode") {
                Ok(json!({
                    "modes": self.get_available_modes(),
                }))
            } else if q.contains("service") {
                Ok(json!({
                    "services": self.get_all_services(),
                }))
            } else {
                self.get_complete_settings().await
            }
        } else {
            self.get_complete_settings().await
        }
    }

    /// Get complete settings overview
    async fn get_complete_settings(&mut self) -> Result<Value> {
        Ok(json!({
            "platforms": self.detect_platforms().await?,
            "modes": self.get_available_modes(),
            "default_domain": "localhost",
            "auto_generate_secrets": true,
            "available_services": self.get_all_services(),
        }))
    }

    /// Complete partial configuration with intelligent defaults
    pub async fn complete_config(&mut self, partial_config: Value) -> Result<Value> {
        let mut config: InstallerConfig = serde_json::from_value(partial_config.clone())
            .context("Failed to parse partial configuration")?;

        // Auto-detect and set platform if missing
        if config.platform.is_none() {
            let platforms = self.detect_platforms().await?;
            if let Some(recommended) = platforms.iter().find(|p| p.recommended) {
                config.platform = Some(recommended.platform.clone());
            }
        }

        // Set default mode if missing
        if config.mode.is_none() {
            config.mode = Some(DeploymentMode::Solo);
        }

        // Set default domain if missing
        if config.domain.is_none() {
            config.domain = Some("localhost".to_string());
        }

        // Add recommended services based on mode
        if config.services.is_empty() {
            if let Some(mode) = &config.mode {
                config.services = self.get_mode_services(mode);
            }
        }

        Ok(serde_json::to_value(config)?)
    }

    /// Validate complete configuration
    pub fn validate_config(&self, config: Value) -> Result<Value> {
        let installer_config: InstallerConfig = serde_json::from_value(config)
            .context("Failed to parse configuration for validation")?;

        let mut errors = Vec::new();
        let mut warnings = Vec::new();

        // Validate platform
        if installer_config.platform.is_none() {
            errors.push("Platform must be specified".to_string());
        }

        // Validate mode
        if installer_config.mode.is_none() {
            errors.push("Deployment mode must be specified".to_string());
        }

        // Validate domain
        if let Some(domain) = &installer_config.domain {
            if domain.is_empty() {
                errors.push("Domain cannot be empty".to_string());
            }
        } else {
            warnings.push("Domain not specified, will use 'localhost'".to_string());
        }

        // Validate services
        if installer_config.services.is_empty() {
            warnings.push("No services selected, will use mode defaults".to_string());
        }

        // Check for required services
        let required_services = vec!["orchestrator", "control-center", "coredns"];
        for required in &required_services {
            if !installer_config
                .services
                .iter()
                .any(|s| s.name == *required)
            {
                warnings.push(format!("Required service '{}' not found", required));
            }
        }

        // Check resource requirements
        if let Some(mode) = &installer_config.mode {
            let min_cpu = mode.min_cpu_cores();
            let min_mem = mode.min_memory_gb();
            warnings.push(format!(
                "Mode '{}' requires minimum {} CPU cores and {} GB RAM",
                mode.to_str(),
                min_cpu,
                min_mem
            ));
        }

        Ok(json!({
            "valid": errors.is_empty(),
            "errors": errors,
            "warnings": warnings,
            "config": installer_config,
        }))
    }

    /// Get mode-specific defaults
    pub fn get_mode_defaults(&self, mode_str: &str) -> Result<Value> {
        let mode = DeploymentMode::from_str(mode_str).ok_or_else(|| {
            ProvisioningError::invalid_input(format!("Invalid mode: {}", mode_str))
        })?;

        Ok(json!({
            "mode": mode.to_str(),
            "description": mode.description(),
            "service_count": mode.service_count(),
            "min_cpu_cores": mode.min_cpu_cores(),
            "min_memory_gb": mode.min_memory_gb(),
            "recommended_services": mode.recommended_services(),
            "auto_generate_secrets": true,
            "domain": "localhost",
        }))
    }

    /// Detect available platforms
    async fn detect_platforms(&mut self) -> Result<Vec<PlatformDetection>> {
        if let Some(cached) = &self.detected_platforms {
            return Ok(cached.clone());
        }

        let mut detections = Vec::new();

        // Detect Docker
        if let Ok(output) = tokio::process::Command::new("docker")
            .arg("--version")
            .output()
            .await
        {
            if output.status.success() {
                let version = String::from_utf8_lossy(&output.stdout)
                    .lines()
                    .next()
                    .unwrap_or("unknown")
                    .to_string();

                detections.push(PlatformDetection {
                    platform: Platform::Docker,
                    available: true,
                    version,
                    recommended: true, // Docker is generally recommended
                });
            }
        }

        // Detect Podman
        if let Ok(output) = tokio::process::Command::new("podman")
            .arg("--version")
            .output()
            .await
        {
            if output.status.success() {
                let version = String::from_utf8_lossy(&output.stdout)
                    .lines()
                    .next()
                    .unwrap_or("unknown")
                    .to_string();

                detections.push(PlatformDetection {
                    platform: Platform::Podman,
                    available: true,
                    version,
                    recommended: detections.is_empty(), // Recommend if Docker not available
                });
            }
        }

        // Detect Kubernetes
        if let Ok(output) = tokio::process::Command::new("kubectl")
            .arg("version")
            .arg("--client")
            .output()
            .await
        {
            if output.status.success() {
                let version = String::from_utf8_lossy(&output.stdout)
                    .lines()
                    .next()
                    .unwrap_or("unknown")
                    .to_string();

                detections.push(PlatformDetection {
                    platform: Platform::Kubernetes,
                    available: true,
                    version,
                    recommended: false, // K8s requires more setup
                });
            }
        }

        // Detect OrbStack (macOS)
        if cfg!(target_os = "macos") {
            if let Ok(output) = tokio::process::Command::new("orb")
                .arg("version")
                .output()
                .await
            {
                if output.status.success() {
                    let version = String::from_utf8_lossy(&output.stdout)
                        .lines()
                        .next()
                        .unwrap_or("unknown")
                        .to_string();

                    detections.push(PlatformDetection {
                        platform: Platform::OrbStack,
                        available: true,
                        version,
                        recommended: true, // OrbStack is great on macOS
                    });
                }
            }
        }

        self.detected_platforms = Some(detections.clone());
        Ok(detections)
    }

    /// Get all available deployment modes
    fn get_available_modes(&self) -> Vec<Value> {
        vec![
            json!({
                "name": "solo",
                "display_name": "Solo",
                "description": DeploymentMode::Solo.description(),
                "service_count": DeploymentMode::Solo.service_count(),
                "min_resources": {
                    "cpu_cores": DeploymentMode::Solo.min_cpu_cores(),
                    "memory_gb": DeploymentMode::Solo.min_memory_gb(),
                }
            }),
            json!({
                "name": "multiuser",
                "display_name": "Multi-User",
                "description": DeploymentMode::MultiUser.description(),
                "service_count": DeploymentMode::MultiUser.service_count(),
                "min_resources": {
                    "cpu_cores": DeploymentMode::MultiUser.min_cpu_cores(),
                    "memory_gb": DeploymentMode::MultiUser.min_memory_gb(),
                }
            }),
            json!({
                "name": "cicd",
                "display_name": "CI/CD",
                "description": DeploymentMode::CICD.description(),
                "service_count": DeploymentMode::CICD.service_count(),
                "min_resources": {
                    "cpu_cores": DeploymentMode::CICD.min_cpu_cores(),
                    "memory_gb": DeploymentMode::CICD.min_memory_gb(),
                }
            }),
            json!({
                "name": "enterprise",
                "display_name": "Enterprise",
                "description": DeploymentMode::Enterprise.description(),
                "service_count": DeploymentMode::Enterprise.service_count(),
                "min_resources": {
                    "cpu_cores": DeploymentMode::Enterprise.min_cpu_cores(),
                    "memory_gb": DeploymentMode::Enterprise.min_memory_gb(),
                }
            }),
        ]
    }

    /// Get all available services
    fn get_all_services(&self) -> Vec<Value> {
        vec![
            json!({"name": "orchestrator", "description": "Task coordination", "port": 8080, "required": true}),
            json!({"name": "control-center", "description": "Web UI", "port": 8081, "required": true}),
            json!({"name": "coredns", "description": "DNS service", "port": 5353, "required": true}),
            json!({"name": "gitea", "description": "Git server", "port": 3000, "required": false}),
            json!({"name": "postgres", "description": "Shared database", "port": 5432, "required": false}),
            json!({"name": "api-server", "description": "REST API", "port": 8083, "required": false}),
            json!({"name": "oci-registry", "description": "OCI Registry (Zot)", "port": 5000, "required": false}),
            json!({"name": "harbor", "description": "Harbor OCI Registry", "port": 5000, "required": false}),
            json!({"name": "kms", "description": "Cosmian KMS", "port": 9998, "required": false}),
            json!({"name": "prometheus", "description": "Metrics", "port": 9090, "required": false}),
            json!({"name": "grafana", "description": "Dashboards", "port": 3001, "required": false}),
            json!({"name": "loki", "description": "Log aggregation", "port": 3100, "required": false}),
            json!({"name": "nginx", "description": "Reverse proxy", "port": 80, "required": false}),
            json!({"name": "mcp-server", "description": "Model Context Protocol", "port": 8084, "required": false}),
            json!({"name": "api-gateway", "description": "REST API access", "port": 8085, "required": false}),
        ]
    }

    /// Get services for a specific mode
    fn get_mode_services(&self, mode: &DeploymentMode) -> Vec<ServiceConfig> {
        let recommended = mode.recommended_services();

        self.get_all_services()
            .into_iter()
            .filter_map(|s| {
                let name = s["name"].as_str()?;
                let description = s["description"].as_str()?;
                let port = s["port"].as_u64()? as u16;
                let required = s["required"].as_bool()?;

                if recommended.contains(&name) {
                    Some(ServiceConfig {
                        name: name.to_string(),
                        description: description.to_string(),
                        port,
                        enabled: true,
                        required,
                    })
                } else {
                    None
                }
            })
            .collect()
    }

    /// Get AI-powered recommendations for platform selection
    pub async fn get_platform_recommendations(&mut self) -> Result<Vec<ConfigRecommendation>> {
        let platforms = self.detect_platforms().await?;
        let mut recommendations = Vec::new();

        for platform in platforms {
            if platform.available {
                let confidence = if platform.recommended { 0.9 } else { 0.6 };

                let reason = match platform.platform {
                    Platform::Docker => "Docker is widely supported and has excellent tooling",
                    Platform::Podman => {
                        "Podman is rootless and more secure for single-user deployments"
                    }
                    Platform::Kubernetes => "Kubernetes provides production-grade orchestration",
                    Platform::OrbStack => {
                        "OrbStack offers native macOS integration with better performance"
                    }
                };

                recommendations.push(ConfigRecommendation {
                    field: "platform".to_string(),
                    recommended_value: json!(platform.platform.to_str()),
                    reason: reason.to_string(),
                    confidence,
                });
            }
        }

        Ok(recommendations)
    }

    /// Get AI-powered service selection recommendations
    pub fn get_service_recommendations(&self, mode: &DeploymentMode) -> Vec<ConfigRecommendation> {
        let recommended_services = mode.recommended_services();

        recommended_services
            .iter()
            .map(|service| {
                let reason = match *service {
                    "orchestrator" => "Required for coordinating infrastructure tasks",
                    "control-center" => "Provides web-based management interface",
                    "coredns" => "Essential for service discovery and DNS resolution",
                    "gitea" => "Enables version control for team collaboration",
                    "postgres" => "Shared database for stateful services",
                    "api-server" => "REST API for programmatic access",
                    "harbor" => "Enterprise-grade OCI registry with security scanning",
                    "prometheus" => "Metrics collection for observability",
                    "grafana" => "Visualization and dashboards",
                    _ => "Recommended for this deployment mode",
                };

                ConfigRecommendation {
                    field: "services".to_string(),
                    recommended_value: json!(service),
                    reason: reason.to_string(),
                    confidence: if service.contains("required") {
                        1.0
                    } else {
                        0.8
                    },
                }
            })
            .collect()
    }

    /// Get resource optimization recommendations
    pub fn get_resource_recommendations(&self, mode: &DeploymentMode) -> Vec<ConfigRecommendation> {
        vec![
            ConfigRecommendation {
                field: "cpu_cores".to_string(),
                recommended_value: json!(mode.min_cpu_cores()),
                reason: format!(
                    "{} mode requires minimum {} CPU cores for optimal performance",
                    mode.to_str(),
                    mode.min_cpu_cores()
                ),
                confidence: 0.95,
            },
            ConfigRecommendation {
                field: "memory_gb".to_string(),
                recommended_value: json!(mode.min_memory_gb()),
                reason: format!(
                    "{} mode requires minimum {} GB RAM to run all services",
                    mode.to_str(),
                    mode.min_memory_gb()
                ),
                confidence: 0.95,
            },
        ]
    }
}

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