use crate::{db, db::log_event};
use serde_json::{json, Value};
use std::path::PathBuf;

/// 单一全局服务器的 settings.json key / 全局注册名
const MCP_KEY: &str = "lian-jing";
/// 旧组级 key 前缀——仅用于注入时清理存量配置，T6 收尾后可删
const LEGACY_KEY_PREFIX: &str = "lian-jing:group-";

// ── 路径解析 ──────────────────────────────────────────────────────────────────

/// 解析项目根目录路径（WSL 项目自动转为 Windows UNC 路径）
fn project_root(project_id: &str) -> Result<PathBuf, String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;

    let row: rusqlite::Result<(Option<String>, Option<String>, Option<String>)> = conn.query_row(
        "SELECT win_path, wsl_path, platform FROM project_workspaces WHERE id = ?1",
        [project_id],
        |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)),
    );

    let (win_path, wsl_path, platform) =
        row.map_err(|_| format!("项目 {} 不存在", project_id))?;

    if platform.as_deref() == Some("wsl") {
        let wsl = wsl_path.ok_or_else(|| "WSL 项目未配置 wsl_path".to_string())?;
        let distro: String = conn
            .query_row(
                "SELECT value FROM settings WHERE key = 'wsl_distro'",
                [],
                |r| r.get(0),
            )
            .unwrap_or_else(|_| "Ubuntu".to_string());
        let inner = wsl.trim_start_matches('/').replace('/', "\\");
        Ok(PathBuf::from(format!(
            "\\\\wsl.localhost\\{}\\{}",
            distro, inner
        )))
    } else {
        let win = win_path.ok_or_else(|| "Windows 项目未配置 win_path".to_string())?;
        Ok(PathBuf::from(win))
    }
}

fn settings_path(project_id: &str) -> Result<PathBuf, String> {
    Ok(project_root(project_id)?.join(".claude").join("settings.json"))
}

fn liangjing_path(project_id: &str) -> Result<PathBuf, String> {
    Ok(project_root(project_id)?.join(".claude").join("liangjing.json"))
}

/// 写炼境上下文文件，供子项目 Claude 会话直接读取 project_id。
/// 合并写入：保留文件中已有的其他字段；主动删除 group_id（单一服务器寻址后已无用，留着会过期误导）
fn write_liangjing_context(project_id: &str, port: u16) -> Result<(), String> {
    let path = liangjing_path(project_id)?;
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)
            .map_err(|e| format!("创建 .claude 目录失败: {}", e))?;
    }
    let mut ctx = match std::fs::read_to_string(&path) {
        Ok(s) => serde_json::from_str::<Value>(&s)
            .ok()
            .filter(|v| v.is_object())
            .unwrap_or_else(|| json!({})),
        Err(_) => json!({}),
    };
    let obj = ctx.as_object_mut().expect("ctx 必为 JSON 对象");
    obj.insert("project_id".into(), json!(project_id));
    obj.insert("mcp_port".into(), json!(port));
    obj.remove("group_id");
    let content = serde_json::to_string_pretty(&ctx).map_err(|e| e.to_string())?;
    std::fs::write(&path, content)
        .map_err(|e| format!("写入 liangjing.json 失败: {}", e))
}

/// 查找 claude CLI 路径（优先环境变量 CLAUDE_BIN，然后常用安装目录，最后 where 命令）
fn find_claude_bin() -> Option<String> {
    if let Ok(path) = std::env::var("CLAUDE_BIN") {
        if std::path::Path::new(&path).exists() {
            return Some(path);
        }
    }

    let home = std::env::var("USERPROFILE")
        .or_else(|_| std::env::var("HOME"))
        .unwrap_or_default();

    let candidates = [
        format!(r"{}\.local\bin\claude.exe", home),
        format!(r"{}\AppData\Local\AnthropicClaude\claude.exe", home),
        r"C:\Program Files\AnthropicClaude\claude.exe".to_string(),
    ];

    for c in &candidates {
        if std::path::Path::new(c).exists() {
            return Some(c.clone());
        }
    }

    // 最后尝试 where 命令（GUI 进程 PATH 可能不含用户 bin，but worth trying）
    if let Ok(out) = std::process::Command::new("where").arg("claude.exe").output() {
        if out.status.success() {
            if let Ok(stdout) = String::from_utf8(out.stdout) {
                if let Some(line) = stdout.lines().next() {
                    return Some(line.trim().to_string());
                }
            }
        }
    }

    None
}

fn claude_git_bash() -> String {
    std::env::var("CLAUDE_CODE_GIT_BASH_PATH")
        .unwrap_or_else(|_| r"C:\Program Files\Git\bin\bash.exe".to_string())
}

/// 将单一全局 lian-jing 服务器注册到 Claude 全局配置（~/.claude.json），best-effort，失败仅记录日志
fn try_register_globally(port: u16) {
    let server_url = format!("http://127.0.0.1:{}/mcp", port);

    let Some(claude_bin) = find_claude_bin() else {
        db::log_event(
            "mcp_inject",
            "warn",
            "claude CLI 未找到，跳过全局 MCP 注册",
            None,
        );
        return;
    };

    let git_bash = claude_git_bash();

    match std::process::Command::new(&claude_bin)
        .args([
            "mcp",
            "add",
            "--transport",
            "sse",
            "-s",
            "user",
            MCP_KEY,
            &server_url,
        ])
        .env("CLAUDE_CODE_GIT_BASH_PATH", &git_bash)
        .output()
    {
        Ok(out) if out.status.success() => {
            db::log_event("mcp_inject", "info", "全局 MCP 注册成功", Some(MCP_KEY));
        }
        Ok(out) => {
            let msg = format!(
                "claude mcp add 非零退出: {}",
                String::from_utf8_lossy(&out.stderr)
            );
            db::log_event("mcp_inject", "warn", &msg, Some(MCP_KEY));
        }
        Err(e) => {
            db::log_event(
                "mcp_inject",
                "warn",
                &format!("执行 claude mcp add 失败: {}", e),
                Some(MCP_KEY),
            );
        }
    }
}

/// 清理旧组级全局注册（lian-jing-{gid}，含完整 UUID 与短 8 位两种历史命名）。
/// 幂等 best-effort：不存在的名字 remove 失败仅忽略。返回成功移除的名字列表。
pub fn cleanup_legacy_global_registrations() -> Vec<String> {
    let Some(claude_bin) = find_claude_bin() else {
        db::log_event("mcp_inject", "warn", "claude CLI 未找到，跳过旧注册清理", None);
        return Vec::new();
    };
    let Ok(conn) = db::pool().get() else {
        return Vec::new();
    };
    let group_ids: Vec<String> = conn
        .prepare("SELECT id FROM product_groups")
        .and_then(|mut stmt| {
            stmt.query_map([], |r| r.get::<_, String>(0))
                .map(|iter| iter.filter_map(|r| r.ok()).collect())
        })
        .unwrap_or_default();

    let git_bash = claude_git_bash();
    let mut removed = Vec::new();
    for gid in &group_ids {
        let mut names = vec![format!("lian-jing-{}", gid)];
        if gid.len() > 8 {
            names.push(format!("lian-jing-{}", &gid[..8]));
        }
        for name in names {
            let ok = std::process::Command::new(&claude_bin)
                .args(["mcp", "remove", "-s", "user", &name])
                .env("CLAUDE_CODE_GIT_BASH_PATH", &git_bash)
                .output()
                .map(|out| out.status.success())
                .unwrap_or(false);
            if ok {
                db::log_event("mcp_inject", "info", "旧组级 MCP 注册已移除", Some(&name));
                removed.push(name);
            }
        }
    }
    removed
}

// ── JSON 读写 ─────────────────────────────────────────────────────────────────

fn read_json(path: &PathBuf) -> Result<Value, String> {
    if !path.exists() {
        return Ok(json!({}));
    }
    let content = std::fs::read_to_string(path)
        .map_err(|e| format!("读取 settings.json 失败: {}", e))?;
    if content.trim().is_empty() {
        return Ok(json!({}));
    }
    serde_json::from_str(&content).map_err(|e| {
        format!(
            "settings.json 格式错误，请手动修复后重试（{}）: {}",
            path.display(),
            e
        )
    })
}

fn write_json(path: &PathBuf, value: &Value) -> Result<(), String> {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)
            .map_err(|e| format!("创建 .claude 目录失败: {}", e))?;
    }
    let content = serde_json::to_string_pretty(value).map_err(|e| e.to_string())?;
    std::fs::write(path, content).map_err(|e| format!("写入 settings.json 失败: {}", e))
}

// ── 公开接口 ──────────────────────────────────────────────────────────────────

/// 向项目的 .claude/settings.json 注入单一 lian-jing MCP 配置，
/// 并清理旧组级 key（迁移内建于重注入）
pub fn inject_project_mcp(project_id: &str) -> Result<(), String> {
    let port = crate::mcp_server::read_port();
    let path = settings_path(project_id)?;
    let mut settings = read_json(&path)?;

    let obj = settings
        .as_object_mut()
        .ok_or("settings.json 顶层必须是 JSON 对象")?;

    // 炼境管理的项目需要无人值守运行（/ship 等技能不应被 permission 弹窗打断）
    obj.entry("permissions")
        .or_insert(json!({"defaultMode": "bypassPermissions"}));

    let servers = obj.entry("mcpServers").or_insert(json!({}));

    if let Some(map) = servers.as_object_mut() {
        map.retain(|k, _| !k.starts_with(LEGACY_KEY_PREFIX));
    }
    servers[MCP_KEY] = json!({
        "type": "sse",
        "url": format!("http://127.0.0.1:{}/mcp", port)
    });

    write_json(&path, &settings).inspect(|_| {
        let ctx = serde_json::json!({"project_id": project_id}).to_string();
        log_event("mcp_inject", "info", "MCP 配置注入成功", Some(&ctx));
    })?;

    // 写炼境上下文，让子项目 Claude 会话无需遍历即可知道 project_id
    let _ = write_liangjing_context(project_id, port);

    // 同步注册到 ~/.claude.json，让 Claude Code 能加载 MCP 工具（best-effort）
    try_register_globally(port);
    Ok(())
}

/// 从项目的 .claude/settings.json 移除炼境 MCP 配置（含旧组级 key）。
/// 调用方需自行保证项目已不属于任何产品组时才调用。
pub fn remove_project_mcp(project_id: &str) -> Result<(), String> {
    let path = settings_path(project_id)?;
    if !path.exists() {
        return Ok(());
    }
    let mut settings = read_json(&path)?;

    if let Some(servers) = settings
        .get_mut("mcpServers")
        .and_then(|v| v.as_object_mut())
    {
        servers.remove(MCP_KEY);
        servers.retain(|k, _| !k.starts_with(LEGACY_KEY_PREFIX));
    }

    write_json(&path, &settings)
}

/// 单一服务器化一次性迁移：批量重注入所有托管项目（换新 key + 清旧 key + liangjing.json 瘦身）
/// 并清理旧组级全局注册。settings 表 mcp_single_migrated 标记位保证只执行一次；
/// 部分项目失败不阻塞（记日志，可对单个项目手动重注入补救）。
pub fn migrate_to_single_server() {
    let already: Option<String> = db::pool().get().ok().and_then(|c| {
        c.query_row(
            "SELECT value FROM settings WHERE key = 'mcp_single_migrated'",
            [],
            |r| r.get(0),
        )
        .ok()
    });
    if already.as_deref() == Some("1") {
        return;
    }

    let errors = reinject_all_mcp();
    let removed = cleanup_legacy_global_registrations();

    let summary = format!(
        "单一服务器迁移完成：重注入失败 {} 个，移除旧全局注册 {} 条{}",
        errors.len(),
        removed.len(),
        if errors.is_empty() {
            String::new()
        } else {
            format!("；失败详情: {}", errors.join("; "))
        }
    );
    let level = if errors.is_empty() { "info" } else { "warn" };
    db::log_event("mcp_inject", level, &summary, None);

    // claude CLI 不在时旧全局注册无法清理——不落标记位，等下次启动重试（整个流程幂等）
    if find_claude_bin().is_none() {
        return;
    }
    if let Ok(conn) = db::pool().get() {
        let _ = conn.execute(
            "INSERT OR REPLACE INTO settings (key, value) VALUES ('mcp_single_migrated', '1')",
            [],
        );
    }
}

/// 端口变更后重新注入所有产品组成员的 MCP 配置
/// 返回注入失败的错误列表（部分失败不影响其他项目）
pub fn reinject_all_mcp() -> Vec<String> {
    let conn = match db::pool().get() {
        Ok(c) => c,
        Err(e) => return vec![e.to_string()],
    };

    let project_ids: Vec<String> = {
        let mut stmt = match conn.prepare("SELECT DISTINCT project_id FROM project_group_map") {
            Ok(s) => s,
            Err(e) => return vec![e.to_string()],
        };
        stmt.query_map([], |row| row.get(0))
            .map(|iter| iter.filter_map(|r| r.ok()).collect())
            .unwrap_or_default()
    };
    drop(conn);

    let mut errors = Vec::new();
    for project_id in project_ids {
        if let Err(e) = inject_project_mcp(&project_id) {
            errors.push(format!("项目 {}: {}", project_id, e));
        }
    }
    errors
}
