//! MCP server import from deep link
//!
//! Handles batch import of MCP server configurations via ccswitch:// URLs.

use super::utils::decode_base64_param;
use super::DeepLinkImportRequest;
use crate::app_config::{McpApps, McpServer};
use crate::error::AppError;
use crate::services::McpService;
use crate::store::AppState;
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// MCP import result
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpImportResult {
    /// Number of successfully imported MCP servers
    pub imported_count: usize,
    /// IDs of successfully imported MCP servers
    pub imported_ids: Vec<String>,
    /// Failed imports with error messages
    pub failed: Vec<McpImportError>,
}

/// MCP import error
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpImportError {
    /// MCP server ID
    pub id: String,
    /// Error message
    pub error: String,
}

/// Import MCP servers from deep link request
///
/// This function handles batch import of MCP servers from standard MCP JSON format.
/// If a server already exists, only the apps flags are merged (existing config preserved).
pub fn import_mcp_from_deeplink(
    state: &AppState,
    request: DeepLinkImportRequest,
) -> Result<McpImportResult, AppError> {
    // Verify this is an MCP request
    if request.resource != "mcp" {
        return Err(AppError::InvalidInput(format!(
            "Expected mcp resource, got '{}'",
            request.resource
        )));
    }

    // Extract and validate apps parameter
    let apps_str = request
        .apps
        .as_ref()
        .ok_or_else(|| AppError::InvalidInput("Missing 'apps' parameter for MCP".to_string()))?;

    // Parse apps into McpApps struct
    let target_apps = parse_mcp_apps(apps_str)?;

    // Extract config
    let config_b64 = request
        .config
        .as_ref()
        .ok_or_else(|| AppError::InvalidInput("Missing 'config' parameter for MCP".to_string()))?;

    // Decode Base64 config
    let decoded = decode_base64_param("config", config_b64)?;

    let config_str = String::from_utf8(decoded)
        .map_err(|e| AppError::InvalidInput(format!("Invalid UTF-8 in config: {e}")))?;

    // Parse JSON
    let config_json: Value = serde_json::from_str(&config_str)
        .map_err(|e| AppError::InvalidInput(format!("Invalid JSON in MCP config: {e}")))?;

    // Extract mcpServers object
    let mcp_servers = config_json
        .get("mcpServers")
        .and_then(|v| v.as_object())
        .ok_or_else(|| {
            AppError::InvalidInput("MCP config must contain 'mcpServers' object".to_string())
        })?;

    if mcp_servers.is_empty() {
        return Err(AppError::InvalidInput(
            "No MCP servers found in config".to_string(),
        ));
    }

    // Get existing servers to check for duplicates
    let existing_servers = state.db.get_all_mcp_servers()?;

    // Import each MCP server
    let mut imported_ids = Vec::new();
    let mut failed = Vec::new();

    for (id, server_spec) in mcp_servers.iter() {
        // Check if server already exists
        let server = if let Some(existing) = existing_servers.get(id) {
            // Server exists - merge apps only, keep other fields unchanged
            log::info!("MCP server '{id}' already exists, merging apps only");

            let mut merged_apps = existing.apps.clone();
            // Merge new apps into existing apps
            if target_apps.claude {
                merged_apps.claude = true;
            }
            if target_apps.codex {
                merged_apps.codex = true;
            }
            if target_apps.gemini {
                merged_apps.gemini = true;
            }

            McpServer {
                id: existing.id.clone(),
                name: existing.name.clone(),
                server: existing.server.clone(), // Keep existing server config
                apps: merged_apps,               // Merged apps
                description: existing.description.clone(),
                homepage: existing.homepage.clone(),
                docs: existing.docs.clone(),
                tags: existing.tags.clone(),
            }
        } else {
            // New server - create with provided config
            log::info!("Creating new MCP server: {id}");
            McpServer {
                id: id.clone(),
                name: id.clone(),
                server: server_spec.clone(),
                apps: target_apps.clone(),
                description: None,
                homepage: None,
                docs: None,
                tags: vec!["imported".to_string()],
            }
        };

        match McpService::upsert_server(state, server) {
            Ok(_) => {
                imported_ids.push(id.clone());
                log::info!("Successfully imported/updated MCP server: {id}");
            }
            Err(e) => {
                failed.push(McpImportError {
                    id: id.clone(),
                    error: format!("{e}"),
                });
                log::warn!("Failed to import MCP server '{id}': {e}");
            }
        }
    }

    Ok(McpImportResult {
        imported_count: imported_ids.len(),
        imported_ids,
        failed,
    })
}

/// Parse apps string into McpApps struct
pub(crate) fn parse_mcp_apps(apps_str: &str) -> Result<McpApps, AppError> {
    let mut apps = McpApps {
        claude: false,
        codex: false,
        gemini: false,
        opencode: false,
        hermes: false,
    };

    for app in apps_str.split(',') {
        match app.trim() {
            "claude" => apps.claude = true,
            "codex" => apps.codex = true,
            "gemini" => apps.gemini = true,
            "opencode" => apps.opencode = true,
            "openclaw" => {
                // OpenClaw doesn't support MCP, ignore silently
                log::debug!("OpenClaw doesn't support MCP, ignoring in apps parameter");
            }
            "hermes" => apps.hermes = true,
            other => {
                return Err(AppError::InvalidInput(format!(
                    "Invalid app in 'apps': {other}"
                )))
            }
        }
    }

    if apps.is_empty() {
        return Err(AppError::InvalidInput(
            "At least one app must be specified in 'apps'".to_string(),
        ));
    }

    Ok(apps)
}
