#!/usr/bin/env -S cargo run --
#![allow(dead_code, unused_imports, unused_variables)]

//! Provisioning MCP Server — raw stdio JSON-RPC 2.0 transport
//!
//! Replaces the broken rust-mcp-sdk transport while keeping all existing
//! tool handlers and business logic from ProvisioningEngine / api_tools.
//!
//! Protocol: newline-delimited JSON-RPC 2.0 on stdin/stdout.
//! All MCP notifications (no `id`) are silently acknowledged.

use std::io::{BufRead, Write};
use std::path::PathBuf;
use std::sync::Arc;

use anyhow::{Context, Result};
use clap::Parser;
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value};
use tokio::sync::Mutex;
use tracing::{debug, error, info, warn};

mod config;
mod errors;
mod provisioning;
mod tools;

use config::Config;
use errors::ProvisioningError;
use provisioning::ProvisioningEngine;
use tools::provisioning_tools::ProvisioningTools as ApiTools;
use tools::{
    ConfigValidatorTool, DocFinderTool, NextActionTool, SettingsTools, SystemStatusTool,
    TroubleshooterTool,
};

// ─── MCP / JSON-RPC types
// ─────────────────────────────────────────────────────

/// Inbound JSON-RPC 2.0 message from the client.
#[derive(Debug, Deserialize)]
struct RpcRequest {
    jsonrpc: Option<String>,
    id: Option<Value>,
    method: String,
    params: Option<Value>,
}

/// Outbound JSON-RPC 2.0 response.
#[derive(Debug, Serialize)]
struct RpcResponse {
    jsonrpc: &'static str,
    id: Value,
    #[serde(skip_serializing_if = "Option::is_none")]
    result: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<RpcError>,
}

#[derive(Debug, Serialize)]
struct RpcError {
    code: i32,
    message: String,
}

impl RpcResponse {
    fn ok(id: Value, result: Value) -> Self {
        Self {
            jsonrpc: "2.0",
            id,
            result: Some(result),
            error: None,
        }
    }

    fn err(id: Value, code: i32, message: String) -> Self {
        Self {
            jsonrpc: "2.0",
            id,
            result: None,
            error: Some(RpcError { code, message }),
        }
    }
}

// ─── CLI ──────────────────────────────────────────────────────────────────────

#[derive(Parser, Debug)]
#[command(name = "provisioning-mcp-server")]
#[command(about = "Rust-native MCP server for Cloud Native Provisioning")]
#[command(version)]
struct Args {
    #[arg(short = 'c', long, env = "PROVISIONING_MCP_CONFIG")]
    config: Option<PathBuf>,
    #[arg(short = 'p', long, env = "PROVISIONING_PATH")]
    provisioning_path: Option<PathBuf>,
    #[arg(short = 'd', long, env = "PROVISIONING_DEBUG")]
    debug: bool,
    #[arg(
        short = 'l',
        long,
        env = "PROVISIONING_LOG_LEVEL",
        default_value = "info"
    )]
    log_level: String,
}

// ─── Server
// ───────────────────────────────────────────────────────────────────

struct McpServer {
    config: Config,
    engine: ProvisioningEngine,
    tools: tools::ProvisioningTools,
    api_tools: ApiTools,
    settings_tools: Arc<Mutex<SettingsTools>>,
    system_status_tool: SystemStatusTool,
    next_action_tool: NextActionTool,
    doc_finder_tool: Arc<Mutex<DocFinderTool>>,
    troubleshooter_tool: TroubleshooterTool,
    config_validator_tool: ConfigValidatorTool,
}

impl McpServer {
    fn new(config: Config) -> Result<Self> {
        let engine = ProvisioningEngine::new(&config)?;
        let tools = tools::ProvisioningTools::new(&config);
        let api_tools = ApiTools::new(Some("http://localhost:3000".to_string()));
        let settings_tools = Arc::new(Mutex::new(SettingsTools::new()));
        let root_str = config.mcp_server.workspace.path.clone();
        let root = std::path::PathBuf::from(root_str.clone());
        let system_status_tool = SystemStatusTool::new(root.clone());
        let next_action_tool = NextActionTool::new(root.clone());
        let doc_finder_tool = Arc::new(Mutex::new(DocFinderTool::new(root.clone())));
        let troubleshooter_tool = TroubleshooterTool::new(root.clone());
        let config_validator_tool = ConfigValidatorTool::new(root);
        Ok(Self {
            config,
            engine,
            tools,
            api_tools,
            settings_tools,
            system_status_tool,
            next_action_tool,
            doc_finder_tool,
            troubleshooter_tool,
            config_validator_tool,
        })
    }

    /// Dispatch a `tools/call` request to the matching handler.
    /// Returns a text string on success, error string on failure.
    #[allow(clippy::excessive_nesting)]
    async fn dispatch(&self, name: &str, args: &Map<String, Value>) -> Result<String, String> {
        macro_rules! req_str {
            ($field:expr) => {
                args.get($field)
                    .and_then(|v| v.as_str())
                    .ok_or_else(|| format!("{} is required", $field))?
            };
        }
        macro_rules! opt_str {
            ($field:expr) => {
                args.get($field).and_then(|v| v.as_str())
            };
        }

        match name {
            // ── Legacy provisioning tools ──────────────────────────────────
            "provision_create_server" => {
                let desc = req_str!("description");
                let result = self
                    .tools
                    .parse_server_description(desc)
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Server configuration:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "provision_ai_template" => {
                let desc = req_str!("description");
                let tmpl = args
                    .get("template_type")
                    .and_then(|v| v.as_str())
                    .unwrap_or("server");
                let complexity = args
                    .get("complexity")
                    .and_then(|v| v.as_str())
                    .unwrap_or("medium");
                let result = self
                    .tools
                    .generate_ai_template(desc, tmpl, complexity)
                    .map_err(|e| e.to_string())?;
                Ok(format!("Generated template:\n\n{}", result))
            }

            "provision_query" => {
                let query = req_str!("query");
                let infra = opt_str!("infra_name");
                let status = self
                    .engine
                    .get_status(infra, false)
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Query: {}\n\nInfrastructure state:\n{}",
                    query, status
                ))
            }

            "provision_component_op" => {
                let component = req_str!("component");
                let operation = req_str!("operation");
                let workspace = req_str!("workspace");
                let check = args
                    .get("check_mode")
                    .and_then(|v| v.as_bool())
                    .unwrap_or(false);
                Ok(format!(
                    "Component '{}' op='{}' workspace='{}' check_mode={} — submitted to orchestrator /api/v1/workflows/component/{}.",
                    component, operation, workspace, check, operation
                ))
            }

            "provision_deploy_taskserv" => {
                // Deprecated — directs callers to provision_component_op.
                let svc = req_str!("service_name");
                Ok(format!(
                    "[DEPRECATED] provision_deploy_taskserv is superseded by provision_component_op.\n\
                     Use: provision_component_op {{ component: \"{}\", operation: \"install\", workspace: \"<ws>\" }}",
                    svc
                ))
            }

            "provision_cluster_create" => {
                let desc = req_str!("cluster_description");
                let kind = req_str!("cluster_type");
                let services: Vec<&str> = args
                    .get("services")
                    .and_then(|v| v.as_array())
                    .map(|a| a.iter().filter_map(|v| v.as_str()).collect())
                    .unwrap_or_default();
                Ok(format!(
                    "Cluster '{}' ({}): {}\nServices: {}",
                    desc,
                    kind,
                    args.get("infra_name")
                        .and_then(|v| v.as_str())
                        .unwrap_or("unnamed"),
                    services.join(", ")
                ))
            }

            "provision_status" => {
                let infra = opt_str!("infra_name");
                let detailed = args
                    .get("detailed")
                    .and_then(|v| v.as_bool())
                    .unwrap_or(false);
                let status = self
                    .engine
                    .get_status(infra, detailed)
                    .map_err(|e| e.to_string())?;
                Ok(format!("Infrastructure status:\n\n{}", status))
            }

            "provision_ai_config" => {
                let action = req_str!("action");
                let result = match action {
                    "status" => self.tools.get_ai_status().map_err(|e| e.to_string())?,
                    "configure" => {
                        let provider = req_str!("provider");
                        self.tools
                            .configure_ai(provider)
                            .map_err(|e| e.to_string())?
                    }
                    "test" => self.tools.test_ai_connection().map_err(|e| e.to_string())?,
                    other => return Err(format!("Unknown AI config action: {}", other)),
                };
                Ok(format!("AI config result:\n\n{}", result))
            }

            // ── Enhanced API tools ─────────────────────────────────────────
            "ai_query" => {
                let query = req_str!("query");
                let ctx = opt_str!("context");
                let result = self
                    .api_tools
                    .ai_query(query, ctx)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "AI Analysis:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_infrastructure_status" => {
                let result = self
                    .api_tools
                    .get_infrastructure_status()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Infrastructure Status:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_system_metrics" => {
                let timerange = opt_str!("timerange");
                let result = self
                    .api_tools
                    .get_system_metrics(timerange)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "System Metrics:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_logs" => {
                let level = opt_str!("level");
                let since = opt_str!("since");
                let result = self
                    .api_tools
                    .get_logs(level, since)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "System Logs:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "start_api_server" => {
                let port = args.get("port").and_then(|v| v.as_u64()).map(|p| p as u16);
                let result = self
                    .api_tools
                    .start_api_server(port)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "API Server:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "create_dashboard" => {
                let template = opt_str!("template");
                let name = opt_str!("name");
                let result = self
                    .api_tools
                    .create_dashboard(template, name)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Dashboard Created:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "start_dashboard" => {
                let name = req_str!("name");
                let port = args.get("port").and_then(|v| v.as_u64()).map(|p| p as u16);
                let result = self
                    .api_tools
                    .start_dashboard(name, port)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Dashboard Started:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "start_ai_agents" => {
                let agents = args
                    .get("agents")
                    .and_then(|v| v.as_array())
                    .map(|a| a.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>());
                let result = self
                    .api_tools
                    .start_ai_agents(agents)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "AI Agents Started:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_agents_status" => {
                let result = self
                    .api_tools
                    .get_agents_status()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Agents Status:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "create_servers" => {
                let config = args
                    .get("config")
                    .ok_or_else(|| "config is required".to_string())?;
                let result = self
                    .api_tools
                    .create_servers(config)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Servers Created:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "list_servers" => {
                let filter = args
                    .get("filter")
                    .and_then(|v| v.as_str())
                    .map(str::to_owned);
                let result = self
                    .api_tools
                    .list_servers(filter.as_deref())
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Servers:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "delete_servers" => {
                let ids: Vec<&str> = args
                    .get("server_ids")
                    .and_then(|v| v.as_array())
                    .map(|a| a.iter().filter_map(|v| v.as_str()).collect())
                    .unwrap_or_default();
                let result = self
                    .api_tools
                    .delete_servers(&ids)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Servers Deleted:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "create_cluster" => {
                let config = args
                    .get("config")
                    .ok_or_else(|| "config is required".to_string())?;
                let result = self
                    .api_tools
                    .create_cluster(config)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Cluster Created:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "generate_infrastructure" => {
                let desc = req_str!("description");
                let output_type = args.get("output_type").and_then(|v| v.as_str());
                let result = self
                    .api_tools
                    .generate_infrastructure(desc, output_type)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Infrastructure Generated:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_cost_optimization" => {
                let result = self
                    .api_tools
                    .get_cost_optimization()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Cost Optimization:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_security_analysis" => {
                let result = self
                    .api_tools
                    .get_security_analysis()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Security Analysis:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "get_performance_analysis" => {
                let result = self
                    .api_tools
                    .get_performance_analysis()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Performance Analysis:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "predict_issues" => {
                let timeframe = args.get("timeframe").and_then(|v| v.as_str());
                let result = self
                    .api_tools
                    .predict_issues(timeframe)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Predicted Issues:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            // ── Installer / settings tools ─────────────────────────────────
            "installer_get_settings" => {
                let mode_str = req_str!("mode");
                let mut tools = self.settings_tools.lock().await;
                let result = tools
                    .get_settings(Some(mode_str))
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Settings:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_complete_config" => {
                let partial = args
                    .get("partial_config")
                    .ok_or_else(|| "partial_config is required".to_string())?
                    .clone();
                let mut tools = self.settings_tools.lock().await;
                let result = tools
                    .complete_config(partial)
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Completed Config:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_validate_config" => {
                let config = args
                    .get("config")
                    .ok_or_else(|| "config is required".to_string())?
                    .clone();
                let tools = self.settings_tools.lock().await;
                let result = tools.validate_config(config).map_err(|e| e.to_string())?;
                Ok(format!(
                    "Validation:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_get_defaults" => {
                let mode_str = req_str!("mode");
                let tools = self.settings_tools.lock().await;
                let result = tools
                    .get_mode_defaults(mode_str)
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Defaults:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_platform_recommendations" => {
                let mut tools = self.settings_tools.lock().await;
                let result = tools
                    .get_platform_recommendations()
                    .await
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Platform Recommendations:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_service_recommendations" => {
                let mode_str = req_str!("mode");
                let tools = self.settings_tools.lock().await;
                let mode = tools::settings::DeploymentMode::from_str(mode_str)
                    .ok_or_else(|| format!("Invalid mode: {}", mode_str))?;
                let result = tools.get_service_recommendations(&mode);
                Ok(format!(
                    "Service Recommendations:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            "installer_resource_recommendations" => {
                let mode_str = req_str!("mode");
                let tools = self.settings_tools.lock().await;
                let mode = tools::settings::DeploymentMode::from_str(mode_str)
                    .ok_or_else(|| format!("Invalid mode: {}", mode_str))?;
                let result = tools.get_resource_recommendations(&mode);
                Ok(format!(
                    "Resource Recommendations:\n\n{}",
                    serde_json::to_string_pretty(&result).unwrap_or_default()
                ))
            }

            // ── Guidance tools ─────────────────────────────────────────────
            "guidance_check_system_status" => {
                let status = self
                    .system_status_tool
                    .check_system()
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "System Status:\n\n{}",
                    serde_json::to_string_pretty(&status).unwrap_or_default()
                ))
            }

            "guidance_suggest_next_action" => {
                let status = self
                    .system_status_tool
                    .check_system()
                    .map_err(|e| e.to_string())?;
                let action = self
                    .next_action_tool
                    .suggest_next_action(&status)
                    .map_err(|e| e.to_string())?;
                Ok(format!(
                    "Next Action:\n\n**Command:** `{}`\n\n**Description:** {}\n\n**Expected \
                     Outcome:** {}\n\n**Documentation:** {}\n\n**Priority:** {}",
                    action.command,
                    action.description,
                    action.expected_outcome,
                    action.docs_link,
                    action.priority
                ))
            }

            "guidance_find_docs" => {
                let query = req_str!("query");
                let ctx = opt_str!("context");
                let doc_finder = self.doc_finder_tool.clone();
                let results = {
                    let mut finder = doc_finder.lock().await;
                    finder.find_docs(query, ctx).map_err(|e| e.to_string())?
                };
                let mut out = String::from("Documentation Results:\n\n");
                if results.is_empty() {
                    out.push_str("No documentation found matching your query.\n");
                } else {
                    for (i, doc) in results.iter().enumerate() {
                        out.push_str(&format!(
                            "{}. **{}** (relevance: {:.1}%)\n   Path: `{}`\n   {}\n\n",
                            i + 1,
                            doc.title,
                            doc.relevance * 100.0,
                            doc.path,
                            doc.snippet.trim()
                        ));
                    }
                }
                Ok(out)
            }

            "guidance_diagnose_issue" => {
                let error_msg = req_str!("error");
                let status = self
                    .system_status_tool
                    .check_system()
                    .map_err(|e| e.to_string())?;
                let diag = self
                    .troubleshooter_tool
                    .diagnose_issue(error_msg, &status)
                    .map_err(|e| e.to_string())?;
                let mut out = format!(
                    "Issue Diagnosis:\n\n**Error:** {}\n\n**Root Cause:** {}\n\n**Confidence:** \
                     {:.1}%\n\n**Suggested Fixes:**\n\n",
                    diag.error,
                    diag.root_cause,
                    diag.confidence * 100.0
                );
                for (i, fix) in diag.fixes.iter().enumerate() {
                    out.push_str(&format!(
                        "{}. {} (Risk: {:?})\n   Commands:\n",
                        i + 1,
                        fix.description,
                        fix.risk
                    ));
                    for cmd in &fix.commands {
                        out.push_str(&format!("   ```\n   {}\n   ```\n", cmd));
                    }
                    out.push_str(&format!(
                        "   Expected outcome: {}\n\n",
                        fix.expected_outcome
                    ));
                }
                if !diag.related_docs.is_empty() {
                    out.push_str("\n**Related Documentation:**\n");
                    for doc in &diag.related_docs {
                        out.push_str(&format!("- [{}]({})\n", doc.title, doc.path));
                    }
                }
                Ok(out)
            }

            "guidance_validate_config_file" => {
                let path = req_str!("config_path");
                let result = self
                    .config_validator_tool
                    .validate_config(path)
                    .map_err(|e| e.to_string())?;
                let mut out = if result.valid {
                    "Configuration Valid\n\n".to_string()
                } else {
                    "Configuration Invalid\n\n".to_string()
                };
                if !result.errors.is_empty() {
                    out.push_str("**Errors:**\n");
                    for e in &result.errors {
                        out.push_str(&format!("- {}\n", e.message));
                        if let Some(fix) = &e.fix {
                            out.push_str(&format!("  Fix: {}\n", fix));
                        }
                    }
                    out.push('\n');
                }
                if !result.warnings.is_empty() {
                    out.push_str("**Warnings:**\n");
                    for w in &result.warnings {
                        out.push_str(&format!(
                            "- {}\n  Recommendation: {}\n",
                            w.message, w.recommendation
                        ));
                    }
                    out.push('\n');
                }
                if !result.suggestions.is_empty() {
                    out.push_str("**Suggestions:**\n");
                    for s in &result.suggestions {
                        out.push_str(&format!("- {}\n", s));
                    }
                    out.push('\n');
                }
                if !result.docs.is_empty() {
                    out.push_str("**Related Documentation:**\n");
                    for doc in &result.docs {
                        out.push_str(&format!("- [{}]({})\n", doc.title, doc.path));
                    }
                }
                Ok(out)
            }

            other => Err(format!("Unknown tool: {}", other)),
        }
    }

    /// Build the MCP tools/list response body.
    fn tool_schemas(&self) -> Value {
        json!({
            "tools": [
                { "name": "provision_create_server",
                  "description": "Create infrastructure servers using natural language or specific configuration",
                  "inputSchema": { "type": "object", "properties": {
                    "description": {"type": "string"},
                    "infra_name": {"type": "string"},
                    "provider": {"type": "string", "enum": ["aws", "upcloud", "local"]},
                    "check_mode": {"type": "boolean", "default": true}
                  }, "required": ["description"] } },
                { "name": "provision_ai_template",
                  "description": "Generate infrastructure templates using AI assistance",
                  "inputSchema": { "type": "object", "properties": {
                    "description": {"type": "string"},
                    "template_type": {"type": "string", "enum": ["server", "taskserv", "cluster", "full-stack"]},
                    "complexity": {"type": "string", "enum": ["simple", "medium", "complex"], "default": "medium"}
                  }, "required": ["description", "template_type"] } },
                { "name": "provision_query",
                  "description": "Query infrastructure state using natural language",
                  "inputSchema": { "type": "object", "properties": {
                    "query": {"type": "string"},
                    "infra_name": {"type": "string"},
                    "output_format": {"type": "string", "enum": ["human", "json", "yaml"], "default": "human"}
                  }, "required": ["query"] } },
                { "name": "provision_component_op",
                  "description": "Unified component lifecycle operation — install, delete, update, reinstall, restart, backup, restore, or check-updates",
                  "inputSchema": { "type": "object", "properties": {
                    "component": {"type": "string", "description": "Component name (e.g. postgresql, forgejo)"},
                    "operation": {"type": "string", "enum": ["install","delete","update","reinstall","restart","backup","restore","check-updates"]},
                    "workspace": {"type": "string", "description": "Workspace name or absolute path"},
                    "infra": {"type": "string", "description": "Infra subdirectory (auto-detected if omitted)"},
                    "server": {"type": "string", "description": "Target server hostname"},
                    "check_mode": {"type": "boolean", "default": false}
                  }, "required": ["component", "operation", "workspace"] } },
                { "name": "provision_deploy_taskserv",
                  "description": "[DEPRECATED — use provision_component_op] Deploy specific infrastructure services",
                  "inputSchema": { "type": "object", "properties": {
                    "service_name": {"type": "string"},
                    "infra_name": {"type": "string"},
                    "check_mode": {"type": "boolean", "default": true}
                  }, "required": ["service_name", "infra_name"] } },
                { "name": "provision_cluster_create",
                  "description": "Create complete clusters with integrated services",
                  "inputSchema": { "type": "object", "properties": {
                    "cluster_description": {"type": "string"},
                    "cluster_type": {"type": "string", "enum": ["development", "staging", "production", "demo"]},
                    "services": {"type": "array", "items": {"type": "string"}},
                    "infra_name": {"type": "string"}
                  }, "required": ["cluster_description", "cluster_type"] } },
                { "name": "provision_status",
                  "description": "Get comprehensive status of infrastructure and services",
                  "inputSchema": { "type": "object", "properties": {
                    "infra_name": {"type": "string"},
                    "detailed": {"type": "boolean", "default": false}
                  } } },
                { "name": "provision_ai_config",
                  "description": "Configure and verify AI capabilities",
                  "inputSchema": { "type": "object", "properties": {
                    "action": {"type": "string", "enum": ["status", "configure", "test"]},
                    "provider": {"type": "string"}
                  }, "required": ["action"] } },
                { "name": "ai_query", "description": "AI-powered infrastructure query",
                  "inputSchema": { "type": "object", "properties": {
                    "query": {"type": "string"}, "context": {"type": "string"}
                  }, "required": ["query"] } },
                { "name": "get_infrastructure_status", "description": "Get current infrastructure status via API",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "get_system_metrics", "description": "Get system performance metrics",
                  "inputSchema": { "type": "object", "properties": {
                    "timerange": {"type": "string"}
                  } } },
                { "name": "get_logs", "description": "Retrieve system logs",
                  "inputSchema": { "type": "object", "properties": {
                    "level": {"type": "string"}, "since": {"type": "string"}
                  } } },
                { "name": "start_api_server", "description": "Start the provisioning API server",
                  "inputSchema": { "type": "object", "properties": {
                    "port": {"type": "integer"}
                  } } },
                { "name": "create_dashboard", "description": "Create a monitoring dashboard",
                  "inputSchema": { "type": "object", "properties": {
                    "template": {"type": "string"}, "name": {"type": "string"}
                  } } },
                { "name": "start_dashboard", "description": "Start a monitoring dashboard",
                  "inputSchema": { "type": "object", "properties": {
                    "name": {"type": "string"}, "port": {"type": "integer"}
                  }, "required": ["name"] } },
                { "name": "start_ai_agents", "description": "Start AI automation agents",
                  "inputSchema": { "type": "object", "properties": {
                    "agents": {"type": "array", "items": {"type": "string"}}
                  } } },
                { "name": "get_agents_status", "description": "Get AI agents status",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "create_servers", "description": "Create servers via API",
                  "inputSchema": { "type": "object", "properties": {
                    "config": {"type": "object"}
                  }, "required": ["config"] } },
                { "name": "list_servers", "description": "List all servers",
                  "inputSchema": { "type": "object", "properties": {
                    "filter": {"type": "object"}
                  } } },
                { "name": "delete_servers", "description": "Delete servers by ID",
                  "inputSchema": { "type": "object", "properties": {
                    "server_ids": {"type": "array", "items": {"type": "string"}}
                  }, "required": ["server_ids"] } },
                { "name": "create_cluster", "description": "Create a cluster via API",
                  "inputSchema": { "type": "object", "properties": {
                    "config": {"type": "object"}
                  }, "required": ["config"] } },
                { "name": "generate_infrastructure", "description": "Generate infrastructure from description",
                  "inputSchema": { "type": "object", "properties": {
                    "description": {"type": "string"}
                  }, "required": ["description"] } },
                { "name": "get_cost_optimization", "description": "Get cost optimization recommendations",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "get_security_analysis", "description": "Get security analysis report",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "get_performance_analysis", "description": "Get performance analysis",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "predict_issues", "description": "Predict potential infrastructure issues",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "installer_get_settings",
                  "description": "Get installer settings for a deployment mode",
                  "inputSchema": { "type": "object", "properties": {
                    "mode": {"type": "string"}
                  }, "required": ["mode"] } },
                { "name": "installer_complete_config",
                  "description": "Complete a partial installer configuration",
                  "inputSchema": { "type": "object", "properties": {
                    "partial_config": {"type": "object"}, "mode": {"type": "string"}
                  }, "required": ["partial_config", "mode"] } },
                { "name": "installer_validate_config",
                  "description": "Validate an installer configuration",
                  "inputSchema": { "type": "object", "properties": {
                    "config": {"type": "object"}, "mode": {"type": "string"}
                  }, "required": ["config", "mode"] } },
                { "name": "installer_get_defaults",
                  "description": "Get default values for a deployment mode",
                  "inputSchema": { "type": "object", "properties": {
                    "mode": {"type": "string"}
                  }, "required": ["mode"] } },
                { "name": "installer_platform_recommendations",
                  "description": "Get platform recommendations for a deployment mode",
                  "inputSchema": { "type": "object", "properties": {
                    "mode": {"type": "string"}
                  }, "required": ["mode"] } },
                { "name": "installer_service_recommendations",
                  "description": "Get service recommendations for a deployment mode",
                  "inputSchema": { "type": "object", "properties": {
                    "mode": {"type": "string"}
                  }, "required": ["mode"] } },
                { "name": "installer_resource_recommendations",
                  "description": "Get resource recommendations for a deployment mode",
                  "inputSchema": { "type": "object", "properties": {
                    "mode": {"type": "string"}
                  }, "required": ["mode"] } },
                { "name": "guidance_check_system_status",
                  "description": "Check overall system health and readiness",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "guidance_suggest_next_action",
                  "description": "Suggest the next recommended action based on system state",
                  "inputSchema": { "type": "object", "properties": {} } },
                { "name": "guidance_find_docs",
                  "description": "Find relevant documentation",
                  "inputSchema": { "type": "object", "properties": {
                    "query": {"type": "string"}, "context": {"type": "string"}
                  }, "required": ["query"] } },
                { "name": "guidance_diagnose_issue",
                  "description": "Diagnose an error or issue and suggest fixes",
                  "inputSchema": { "type": "object", "properties": {
                    "error": {"type": "string"}
                  }, "required": ["error"] } },
                { "name": "guidance_validate_config_file",
                  "description": "Validate a configuration file and report issues",
                  "inputSchema": { "type": "object", "properties": {
                    "config_path": {"type": "string"}
                  }, "required": ["config_path"] } }
            ]
        })
    }
}

// ─── Transport
// ────────────────────────────────────────────────────────────────

async fn run_stdio_server(server: McpServer) -> Result<()> {
    let stdin = std::io::stdin();
    let stdout = std::io::stdout();
    let mut out = std::io::BufWriter::new(stdout.lock());

    info!("MCP server ready on stdio");

    for line in stdin.lock().lines() {
        let line = line.context("stdin read error")?;
        let trimmed = line.trim();
        if trimmed.is_empty() {
            continue;
        }

        let request: RpcRequest = match serde_json::from_str(trimmed) {
            Ok(r) => r,
            Err(e) => {
                // Parse error: try to extract id from raw value
                let raw: Value = serde_json::from_str(trimmed).unwrap_or(Value::Null);
                let id = raw.get("id").cloned().unwrap_or(Value::Null);
                let resp = RpcResponse::err(id, -32700, format!("Parse error: {}", e));
                writeln!(out, "{}", serde_json::to_string(&resp)?)?;
                out.flush()?;
                continue;
            }
        };

        // Notifications (no id) are silently accepted — no response sent.
        let id = match &request.id {
            Some(id) => id.clone(),
            None => {
                debug!("Notification received: {}", request.method);
                continue;
            }
        };

        let response = handle_request(&server, id, &request.method, request.params).await;
        writeln!(out, "{}", serde_json::to_string(&response)?)?;
        out.flush()?;
    }

    Ok(())
}

async fn handle_request(
    server: &McpServer,
    id: Value,
    method: &str,
    params: Option<Value>,
) -> RpcResponse {
    match method {
        "initialize" => RpcResponse::ok(
            id,
            json!({
                "protocolVersion": "2024-11-05",
                "serverInfo": {
                    "name": "provisioning-mcp-server",
                    "version": env!("CARGO_PKG_VERSION")
                },
                "capabilities": {
                    "tools": { "listChanged": false }
                }
            }),
        ),

        "tools/list" => RpcResponse::ok(id, server.tool_schemas()),

        "tools/call" => {
            let params = params.unwrap_or(Value::Null);
            let name = match params.get("name").and_then(|v| v.as_str()) {
                Some(n) => n.to_string(),
                None => return RpcResponse::err(id, -32602, "Missing tool name".to_string()),
            };
            let args: Map<String, Value> = params
                .get("arguments")
                .and_then(|v| v.as_object())
                .cloned()
                .unwrap_or_default();

            info!("tools/call: {}", name);
            match server.dispatch(&name, &args).await {
                Ok(text) => RpcResponse::ok(
                    id,
                    json!({
                        "content": [{ "type": "text", "text": text }],
                        "isError": false
                    }),
                ),
                Err(e) => {
                    warn!("Tool error for '{}': {}", name, e);
                    RpcResponse::ok(
                        id,
                        json!({
                            "content": [{ "type": "text", "text": e }],
                            "isError": true
                        }),
                    )
                }
            }
        }

        "ping" => RpcResponse::ok(id, json!({})),

        other => {
            debug!("Unknown method: {}", other);
            RpcResponse::err(id, -32601, format!("Method not found: {}", other))
        }
    }
}

// ─── Entry point
// ──────────────────────────────────────────────────────────────

#[tokio::main]
async fn main() -> Result<()> {
    let args = Args::parse();

    // Initialize centralized observability (logging, metrics, health checks)
    let _guard = observability::init_from_env("mcp-server", env!("CARGO_PKG_VERSION"))?;

    // Try to load mcp-server.ncl
    if let Ok(config) = platform_config::load_service_config_from_ncl("mcp-server") {
        info!("✓ Loaded mcp-server configuration from NCL");
        eprintln!("Config: {:?}", config);
    }

    let config = Config::load()?;
    info!(
        "Provisioning MCP Server starting — host: {}, port: {}",
        config.mcp_server.server.host, config.mcp_server.server.port
    );

    let server = McpServer::new(config)?;
    run_stdio_server(server).await
}

#[cfg(test)]
mod tests {
    use std::fs;

    use tempfile::TempDir;

    use super::*;

    fn test_config() -> (Config, TempDir) {
        let tmp = TempDir::new().unwrap();
        let core = tmp.path().join("core/nulib");
        fs::create_dir_all(&core).unwrap();
        fs::write(core.join("provisioning"), "#!/bin/bash\necho test").unwrap();
        let cfg = Config {
            provisioning_path: tmp.path().to_path_buf(),
            ai: config::AIConfig::default(),
            server: config::ServerConfig::default(),
            debug: false,
        };
        (cfg, tmp)
    }

    #[test]
    fn rpc_response_serializes_ok() {
        let r = RpcResponse::ok(json!(1), json!({"ok": true}));
        let s = serde_json::to_string(&r).unwrap();
        assert!(s.contains("\"jsonrpc\":\"2.0\""));
        assert!(s.contains("\"id\":1"));
        assert!(!s.contains("\"error\""));
    }

    #[test]
    fn rpc_response_serializes_error() {
        let r = RpcResponse::err(json!(2), -32601, "not found".to_string());
        let s = serde_json::to_string(&r).unwrap();
        assert!(s.contains("\"error\""));
        assert!(!s.contains("\"result\""));
    }

    #[test]
    fn tool_schemas_contains_all_expected_tools() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let schemas = server.tool_schemas();
        let tools = schemas["tools"].as_array().unwrap();
        let names: Vec<&str> = tools.iter().filter_map(|t| t["name"].as_str()).collect();
        for expected in &[
            "provision_create_server",
            "provision_status",
            "guidance_check_system_status",
            "guidance_diagnose_issue",
            "installer_get_settings",
            "ai_query",
        ] {
            assert!(names.contains(expected), "Missing tool: {}", expected);
        }
        assert!(
            names.len() >= 35,
            "Expected at least 35 tools, got {}",
            names.len()
        );
    }

    #[tokio::test]
    async fn dispatch_unknown_tool_returns_error() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let result = server.dispatch("nonexistent_tool", &Map::new()).await;
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Unknown tool"));
    }

    #[tokio::test]
    async fn dispatch_provision_status_no_args() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let result = server.dispatch("provision_status", &Map::new()).await;
        // May succeed or fail depending on provisioning state — not an error about the
        // tool itself
        match result {
            Ok(_) => {}
            Err(e) => assert!(!e.contains("Unknown tool"), "Unexpected error: {}", e),
        }
    }

    #[tokio::test]
    async fn handle_initialize_returns_protocol_version() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let resp = handle_request(&server, json!(1), "initialize", None).await;
        assert!(resp.error.is_none());
        let result = resp.result.unwrap();
        assert_eq!(result["protocolVersion"], "2024-11-05");
    }

    #[tokio::test]
    async fn handle_tools_list_returns_tools() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let resp = handle_request(&server, json!(2), "tools/list", None).await;
        assert!(resp.error.is_none());
        let tools = resp.result.unwrap()["tools"].as_array().unwrap().len();
        assert!(tools >= 35);
    }

    #[tokio::test]
    async fn handle_unknown_method_returns_error() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let resp = handle_request(&server, json!(3), "unknown/method", None).await;
        assert!(resp.error.is_some());
        assert_eq!(resp.error.unwrap().code, -32601);
    }

    #[tokio::test]
    async fn tools_call_missing_name_returns_error() {
        let (cfg, _tmp) = test_config();
        let server = McpServer::new(cfg).unwrap();
        let resp = handle_request(
            &server,
            json!(4),
            "tools/call",
            Some(json!({"arguments": {}})),
        )
        .await;
        assert!(resp.error.is_some());
    }
}
