use crate::db::pool;
use rusqlite::params;
use serde::{Deserialize, Serialize};

// ── 数据结构 ──────────────────────────────────────────────────────────────────

#[derive(Debug, Serialize, Clone)]
pub struct ScannedTool {
    pub id: String,
    pub name: String,
    pub category: String, // editor / runtime / pkg_manager / version_manager / cli / database
    pub cmd: String,
    pub path: Option<String>,
    pub version: Option<String>,
    pub found: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct DevToolRecord {
    pub id: String,
    pub name: String,
    pub category: String,
    pub cmd: Option<String>,
    pub path: Option<String>,
    pub version: Option<String>,
    pub note: Option<String>,
    pub env: String,
    pub space_id: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct SaveDevToolInput {
    pub id: String,
    pub name: String,
    pub category: String,
    pub cmd: Option<String>,
    pub path: Option<String>,
    pub version: Option<String>,
    pub note: Option<String>,
    pub env: Option<String>,
    pub space_id: Option<String>,
}

// ── 内置工具特征库 ─────────────────────────────────────────────────────────────

struct ToolSpec {
    id: &'static str,
    name: &'static str,
    category: &'static str,
    cmd: &'static str,
    version_flag: &'static str,
    /// Windows 常见安装路径（fallback，当 PATH 里找不到时）
    fallback_paths: &'static [&'static str],
}

fn tool_specs() -> Vec<ToolSpec> {
    vec![
        // 编辑器 / IDE
        ToolSpec { id: "cursor",   name: "Cursor",        category: "editor",          cmd: "cursor",    version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "vscode",   name: "VS Code",       category: "editor",          cmd: "code",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "windsurf", name: "Windsurf",      category: "editor",          cmd: "windsurf",  version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "vim",      name: "Vim",           category: "editor",          cmd: "vim",       version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "nvim",     name: "Neovim",        category: "editor",          cmd: "nvim",      version_flag: "--version", fallback_paths: &[] },
        // 语言运行时
        ToolSpec { id: "node",    name: "Node.js",        category: "runtime",         cmd: "node",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "python",  name: "Python",         category: "runtime",         cmd: "python",    version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "python3", name: "Python 3",       category: "runtime",         cmd: "python3",   version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "go",      name: "Go",             category: "runtime",         cmd: "go",        version_flag: "version",   fallback_paths: &["C:\\Program Files\\Go\\bin\\go.exe"] },
        ToolSpec { id: "rustc",   name: "Rust (rustc)",   category: "runtime",         cmd: "rustc",     version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "java",    name: "Java",           category: "runtime",         cmd: "java",      version_flag: "-version",  fallback_paths: &[] },
        ToolSpec { id: "ruby",    name: "Ruby",           category: "runtime",         cmd: "ruby",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "deno",    name: "Deno",           category: "runtime",         cmd: "deno",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "bun",     name: "Bun",            category: "runtime",         cmd: "bun",       version_flag: "--version", fallback_paths: &[] },
        // 包管理器
        ToolSpec { id: "pnpm",    name: "pnpm",           category: "pkg_manager",     cmd: "pnpm",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "npm",     name: "npm",            category: "pkg_manager",     cmd: "npm",       version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "yarn",    name: "Yarn",           category: "pkg_manager",     cmd: "yarn",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "cargo",   name: "Cargo",          category: "pkg_manager",     cmd: "cargo",     version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "pip",     name: "pip",            category: "pkg_manager",     cmd: "pip",       version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "pip3",    name: "pip3",           category: "pkg_manager",     cmd: "pip3",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "composer", name: "Composer",      category: "pkg_manager",     cmd: "composer",  version_flag: "--version", fallback_paths: &[] },
        // 版本管理器
        ToolSpec { id: "rustup",  name: "rustup",         category: "version_manager", cmd: "rustup",    version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "fnm",     name: "fnm",            category: "version_manager", cmd: "fnm",       version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "volta",   name: "Volta",          category: "version_manager", cmd: "volta",     version_flag: "--version", fallback_paths: &[] },
        // CLI 工具
        ToolSpec { id: "git",       name: "Git",          category: "cli",             cmd: "git",       version_flag: "--version", fallback_paths: &["C:\\Program Files\\Git\\bin\\git.exe"] },
        ToolSpec { id: "claude",    name: "Claude CLI",   category: "cli",             cmd: "claude",    version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "docker",    name: "Docker",       category: "cli",             cmd: "docker",    version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "gh",        name: "GitHub CLI",   category: "cli",             cmd: "gh",        version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "kubectl",   name: "kubectl",      category: "cli",             cmd: "kubectl",   version_flag: "version",   fallback_paths: &[] },
        ToolSpec { id: "terraform", name: "Terraform",    category: "cli",             cmd: "terraform", version_flag: "version",   fallback_paths: &[] },
        ToolSpec { id: "make",      name: "Make",         category: "cli",             cmd: "make",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "az",        name: "Azure CLI",    category: "cli",             cmd: "az",        version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "aws",       name: "AWS CLI",      category: "cli",             cmd: "aws",       version_flag: "--version", fallback_paths: &[] },
        // 数据库客户端
        ToolSpec { id: "mysql",      name: "MySQL CLI",     category: "database",      cmd: "mysql",     version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "psql",       name: "PostgreSQL CLI",category: "database",      cmd: "psql",      version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "redis-cli",  name: "Redis CLI",     category: "database",      cmd: "redis-cli", version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "sqlite3",    name: "SQLite",        category: "database",      cmd: "sqlite3",   version_flag: "--version", fallback_paths: &[] },
        ToolSpec { id: "mongosh",    name: "MongoDB Shell", category: "database",      cmd: "mongosh",   version_flag: "--version", fallback_paths: &[] },
    ]
}

// ── 辅助函数 ──────────────────────────────────────────────────────────────────

/// 在 Windows PATH 里查找可执行文件路径
fn find_in_path(cmd: &str) -> Option<String> {
    #[cfg(target_os = "windows")]
    let output = crate::silent_cmd("where").arg(cmd).output().ok()?;
    #[cfg(not(target_os = "windows"))]
    let output = crate::silent_cmd("which").arg(cmd).output().ok()?;

    if output.status.success() {
        let path = String::from_utf8_lossy(&output.stdout)
            .lines()
            .next()
            .map(|s| s.trim().to_string())?;
        if path.is_empty() { None } else { Some(path) }
    } else {
        None
    }
}

/// 尝试获取 Windows 工具版本号
fn get_version(cmd: &str, flag: &str) -> Option<String> {
    let output = crate::silent_cmd(cmd).arg(flag).output().ok()?;
    let raw = if output.stdout.is_empty() {
        String::from_utf8_lossy(&output.stderr).to_string()
    } else {
        String::from_utf8_lossy(&output.stdout).to_string()
    };
    let line = raw.lines().next()?.trim().to_string();
    if line.is_empty() { None } else { Some(line.chars().take(80).collect()) }
}

/// 在 WSL 中查找可执行文件路径（使用登录 shell，确保 nvm 等 PATH 已加载）
fn find_in_wsl(cmd: &str) -> Option<String> {
    let output = crate::silent_cmd("wsl")
        .args(["bash", "-l", "-c", &format!("which {cmd}")])
        .output()
        .ok()?;
    if output.status.success() {
        let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
        if path.is_empty() { None } else { Some(path) }
    } else {
        None
    }
}

/// 尝试获取 WSL 工具版本号（使用登录 shell）
fn get_wsl_version(cmd: &str, flag: &str) -> Option<String> {
    let output = crate::silent_cmd("wsl")
        .args(["bash", "-l", "-c", &format!("{cmd} {flag}")])
        .output()
        .ok()?;
    let raw = if output.stdout.is_empty() {
        String::from_utf8_lossy(&output.stderr).to_string()
    } else {
        String::from_utf8_lossy(&output.stdout).to_string()
    };
    let line = raw.lines().next()?.trim().to_string();
    if line.is_empty() { None } else { Some(line.chars().take(80).collect()) }
}

// ── Tauri 命令 ────────────────────────────────────────────────────────────────

/// 扫描当前系统中已安装的开发工具
#[tauri::command]
pub fn scan_dev_tools() -> Vec<ScannedTool> {
    let specs = tool_specs();
    let mut result = Vec::with_capacity(specs.len());

    for spec in &specs {
        let path = find_in_path(spec.cmd)
            .or_else(|| {
                // fallback: 检查固定路径
                spec.fallback_paths.iter().find_map(|p| {
                    if std::path::Path::new(p).exists() {
                        Some(p.to_string())
                    } else {
                        None
                    }
                })
            });

        let found = path.is_some();
        let version = if found {
            get_version(spec.cmd, spec.version_flag)
        } else {
            None
        };

        result.push(ScannedTool {
            id: spec.id.to_string(),
            name: spec.name.to_string(),
            category: spec.category.to_string(),
            cmd: spec.cmd.to_string(),
            path,
            version,
            found,
        });
    }

    result
}

/// 获取已保存的工具列表（按 env + space_id 过滤）
#[tauri::command]
pub fn get_dev_tools(env: String, space_id: Option<String>) -> Result<Vec<DevToolRecord>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let rows = match &space_id {
        Some(sid) => {
            let mut stmt = conn
                .prepare("SELECT id, name, category, cmd, path, version, note, env, space_id FROM dev_tools WHERE env = ?1 AND space_id = ?2 ORDER BY category, name")
                .map_err(|e| e.to_string())?;
            let x = stmt
                .query_map(params![env, sid], |row| {
                    Ok(DevToolRecord {
                        id: row.get(0)?, name: row.get(1)?, category: row.get(2)?,
                        cmd: row.get(3)?, path: row.get(4)?, version: row.get(5)?,
                        note: row.get(6)?, env: row.get(7)?, space_id: row.get(8)?,
                    })
                })
                .map_err(|e| e.to_string())?
                .collect::<Result<Vec<_>, _>>()
                .map_err(|e| e.to_string())?; x
        }
        None => {
            let mut stmt = conn
                .prepare("SELECT id, name, category, cmd, path, version, note, env, space_id FROM dev_tools WHERE env = ?1 AND space_id IS NULL ORDER BY category, name")
                .map_err(|e| e.to_string())?;
            let x = stmt
                .query_map(params![env], |row| {
                    Ok(DevToolRecord {
                        id: row.get(0)?, name: row.get(1)?, category: row.get(2)?,
                        cmd: row.get(3)?, path: row.get(4)?, version: row.get(5)?,
                        note: row.get(6)?, env: row.get(7)?, space_id: row.get(8)?,
                    })
                })
                .map_err(|e| e.to_string())?
                .collect::<Result<Vec<_>, _>>()
                .map_err(|e| e.to_string())?; x
        }
    };
    Ok(rows)
}

/// 批量保存选中的工具（已存在则更新）
/// 当 space_id 存在时，DB id 前缀为 "{space_id}:{original_id}"，保证跨空间不冲突
#[tauri::command]
pub fn save_dev_tools(tools: Vec<SaveDevToolInput>) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    for t in tools {
        let env = t.env.clone().unwrap_or_else(|| "windows".to_string());
        let db_id = match &t.space_id {
            Some(sid) => format!("{}:{}", sid, t.id),
            None => t.id.clone(),
        };
        conn.execute(
            "INSERT INTO dev_tools (id, name, category, cmd, path, version, note, env, space_id)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
             ON CONFLICT(id) DO UPDATE SET
               name     = excluded.name,
               category = excluded.category,
               cmd      = excluded.cmd,
               path     = excluded.path,
               version  = excluded.version,
               note     = excluded.note,
               env      = excluded.env,
               space_id = excluded.space_id",
            params![db_id, t.name, t.category, t.cmd, t.path, t.version, t.note, env, t.space_id],
        )
        .map_err(|e| e.to_string())?;
    }
    Ok(())
}

/// 删除已保存的工具
#[tauri::command]
pub fn delete_dev_tool(id: String) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM dev_tools WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    Ok(())
}

/// 手动添加或更新单个工具
#[tauri::command]
pub fn upsert_dev_tool(tool: SaveDevToolInput) -> Result<(), String> {
    save_dev_tools(vec![tool])
}

/// 扫描 WSL 环境中已安装的开发工具
#[tauri::command]
pub fn scan_wsl_dev_tools() -> Vec<ScannedTool> {
    let specs = tool_specs();
    let mut result = Vec::with_capacity(specs.len());

    for spec in &specs {
        let path = find_in_wsl(spec.cmd);
        let found = path.is_some();
        let version = if found {
            get_wsl_version(spec.cmd, spec.version_flag)
        } else {
            None
        };

        result.push(ScannedTool {
            id: format!("{}-wsl", spec.id),
            name: spec.name.to_string(),
            category: spec.category.to_string(),
            cmd: spec.cmd.to_string(),
            path,
            version,
            found,
        });
    }

    result
}
