use anyhow::Result;
use std::path::{Path, PathBuf};

// ── Public entry point ────────────────────────────────────────────────────────

pub fn handle(agent: Option<String>) -> Result<()> {
    match agent.as_deref() {
        Some(name) => uninstall_agent(name)?,
        None => {
            let mut removed = 0u32;
            for name in SUPPORTED_AGENTS {
                if is_configured(name) {
                    println!("Removing: {name}");
                    uninstall_agent(name)?;
                    removed += 1;
                    println!();
                }
            }
            if removed == 0 {
                println!("No bctx agent configurations found.");
                println!("Run with --agent to remove a specific agent:");
                for name in SUPPORTED_AGENTS {
                    println!("  bctx uninstall --agent {name}");
                }
            }
        }
    }
    Ok(())
}

// Same list as init.rs — must stay in sync.
const SUPPORTED_AGENTS: &[&str] = &[
    "claude",
    "cursor",
    "windsurf",
    "gemini",
    "zed",
    "continue",
    "cline",
    "roo",
    "copilot",
    "antigravity",
    "amazonq",
    "kiro",
    "codex",
    "trae",
    "zsh",
    "bash",
];

// ── Detection (was bctx ever configured for this agent?) ─────────────────────

fn is_configured(agent: &str) -> bool {
    let home = super::home_dir();
    let h = |rel: &str| PathBuf::from(&home).join(rel);

    match agent {
        "claude" => {
            has_bctx_mcp_key(&h(".claude.json"), "mcpServers")
                || has_bctx_mcp_key(&h(".claude/mcp.json"), "mcpServers")
                || h(".claude/bctx-hook.sh").exists()
        }
        "cursor" => has_bctx_mcp_key(&h(".cursor/mcp.json"), "mcpServers"),
        "windsurf" => has_bctx_mcp_key(&h(".codeium/windsurf/mcp_config.json"), "mcpServers"),
        "gemini" => has_bctx_mcp_key(&h(".gemini/settings.json"), "mcpServers"),
        "zed" => has_bctx_mcp_key(&h(".config/zed/settings.json"), "context_servers"),
        "continue" => has_bctx_continue_entry(&h(".continue/config.json")),
        "cline" | "roo" | "copilot" => {
            #[cfg(target_os = "macos")]
            {
                let p = PathBuf::from(&home).join("Library/Application Support/Code/User/mcp.json");
                has_bctx_mcp_key(&p, "servers")
            }
            #[cfg(not(target_os = "macos"))]
            {
                let p = h(".config/Code/User/mcp.json");
                has_bctx_mcp_key(&p, "servers")
            }
        }
        "antigravity" => has_bctx_mcp_key(&h(".gemini/antigravity/mcp_config.json"), "mcpServers"),
        "amazonq" => has_bctx_mcp_key(&h(".aws/amazonq/mcp.json"), "mcpServers"),
        "kiro" => has_bctx_mcp_key(&h(".kiro/settings/mcp.json"), "mcpServers"),
        "codex" => has_bctx_mcp_key(&h(".codex/mcp.json"), "mcpServers"),
        "trae" => {
            #[cfg(target_os = "macos")]
            {
                let p = PathBuf::from(&home).join("Library/Application Support/Trae/User/mcp.json");
                has_bctx_mcp_key(&p, "servers")
            }
            #[cfg(not(target_os = "macos"))]
            false
        }
        "zsh" => shell_has_bctx_hook(&h(".zshrc")),
        "bash" => shell_has_bctx_hook(&h(".bashrc")) || shell_has_bctx_hook(&h(".bash_profile")),
        _ => false,
    }
}

fn has_bctx_mcp_key(path: &Path, key: &str) -> bool {
    let Ok(raw) = std::fs::read_to_string(path) else {
        return false;
    };
    let Ok(cfg) = serde_json::from_str::<serde_json::Value>(&raw) else {
        return false;
    };
    cfg[key].get("bctx").is_some()
}

fn has_bctx_continue_entry(path: &Path) -> bool {
    let Ok(raw) = std::fs::read_to_string(path) else {
        return false;
    };
    let Ok(cfg) = serde_json::from_str::<serde_json::Value>(&raw) else {
        return false;
    };
    cfg["mcpServers"].as_array().is_some_and(|arr| {
        arr.iter()
            .any(|e| e.get("name").and_then(|v| v.as_str()) == Some("bctx"))
    })
}

fn shell_has_bctx_hook(path: &Path) -> bool {
    std::fs::read_to_string(path).is_ok_and(|s| s.contains("# bctx —"))
}

// ── Dispatch ──────────────────────────────────────────────────────────────────

fn uninstall_agent(agent: &str) -> Result<()> {
    match agent {
        "claude" => uninstall_claude(),
        "cursor" => uninstall_cursor(),
        "windsurf" => uninstall_windsurf(),
        "gemini" => uninstall_gemini(),
        "zed" => uninstall_zed(),
        "continue" => uninstall_continue(),
        "cline" | "roo" => uninstall_vscode_extension(agent),
        "copilot" => uninstall_vscode_extension("copilot"),
        "antigravity" => uninstall_antigravity(),
        "amazonq" => uninstall_amazonq(),
        "kiro" => uninstall_kiro(),
        "codex" => uninstall_codex(),
        "trae" => uninstall_trae(),
        "zsh" => uninstall_shell("zsh"),
        "bash" => uninstall_shell("bash"),
        other => {
            eprintln!("bctx uninstall: unknown agent '{other}'");
            eprintln!("Supported agents:");
            for name in SUPPORTED_AGENTS {
                eprintln!("  {name}");
            }
            std::process::exit(1);
        }
    }
}

// ── Claude Code ───────────────────────────────────────────────────────────────

fn uninstall_claude() -> Result<()> {
    let home = super::home_dir();
    let claude_dir = PathBuf::from(&home).join(".claude");

    // ~/.claude.json
    let claude_json = PathBuf::from(&home).join(".claude.json");
    remove_mcp_key(&claude_json, "mcpServers")?;

    // ~/.claude/mcp.json (Claude Code desktop / current CLI format)
    let claude_mcp = claude_dir.join("mcp.json");
    remove_mcp_key(&claude_mcp, "mcpServers")?;

    // VSCode extension mcp.json (macOS)
    #[cfg(target_os = "macos")]
    remove_vscode_mcp_entry(&home, "Code/User")?;

    // settings.json — remove bctx-hook entry from PreToolUse
    let settings_path = claude_dir.join("settings.json");
    if settings_path.exists() {
        let raw = std::fs::read_to_string(&settings_path)?;
        let mut cfg: serde_json::Value =
            serde_json::from_str(&raw).unwrap_or(serde_json::json!({}));
        if let Some(arr) = cfg["hooks"]["PreToolUse"].as_array_mut() {
            let before = arr.len();
            arr.retain(|h| {
                !h["hooks"]
                    .as_array()
                    .and_then(|hs| hs.first())
                    .and_then(|h| h["command"].as_str())
                    .is_some_and(|c| c.contains("bctx-hook"))
            });
            if arr.len() < before {
                std::fs::write(&settings_path, serde_json::to_string_pretty(&cfg)?)?;
                println!("  ✓ settings.json hook entry removed");
            }
        }
    }

    // bctx-hook.sh
    let hook_path = claude_dir.join("bctx-hook.sh");
    if hook_path.exists() {
        std::fs::remove_file(&hook_path)?;
        println!("  ✓ bctx-hook.sh removed");
    }

    println!("  → Restart Claude Code to deactivate.");
    Ok(())
}

// ── Cursor ────────────────────────────────────────────────────────────────────

fn uninstall_cursor() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".cursor/mcp.json");
    remove_mcp_key(&path, "mcpServers")?;

    #[cfg(target_os = "macos")]
    remove_vscode_mcp_entry(&home, "Cursor/User")?;

    println!("  → Restart Cursor to deactivate.");
    Ok(())
}

// ── Windsurf ──────────────────────────────────────────────────────────────────

fn uninstall_windsurf() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".codeium/windsurf/mcp_config.json");
    remove_mcp_key(&path, "mcpServers")?;

    #[cfg(target_os = "macos")]
    remove_vscode_mcp_entry(&home, "Windsurf/User")?;

    println!("  → Restart Windsurf to deactivate.");
    Ok(())
}

// ── Gemini CLI ────────────────────────────────────────────────────────────────

fn uninstall_gemini() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".gemini/settings.json");
    remove_mcp_key(&path, "mcpServers")?;
    println!("  → Restart Gemini CLI to deactivate.");
    Ok(())
}

// ── Zed ───────────────────────────────────────────────────────────────────────

fn uninstall_zed() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".config/zed/settings.json");
    remove_mcp_key(&path, "context_servers")?;
    println!("  → Restart Zed to deactivate.");
    Ok(())
}

// ── Continue ──────────────────────────────────────────────────────────────────

fn uninstall_continue() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".continue/config.json");
    if !path.exists() {
        return Ok(());
    }
    let raw = std::fs::read_to_string(&path)?;
    let mut cfg: serde_json::Value = serde_json::from_str(&raw).unwrap_or(serde_json::json!({}));
    if let Some(arr) = cfg["mcpServers"].as_array_mut() {
        let before = arr.len();
        arr.retain(|e| e.get("name").and_then(|v| v.as_str()) != Some("bctx"));
        if arr.len() < before {
            std::fs::write(&path, serde_json::to_string_pretty(&cfg)?)?;
            println!("  ✓ ~/.continue/config.json — bctx entry removed");
        }
    }
    println!("  → Reload Continue extension to deactivate.");
    Ok(())
}

// ── VSCode extensions (Cline, Roo Code, Copilot) ─────────────────────────────

fn uninstall_vscode_extension(label: &str) -> Result<()> {
    let home = super::home_dir();

    #[cfg(target_os = "macos")]
    remove_vscode_mcp_entry(&home, "Code/User")?;

    #[cfg(not(target_os = "macos"))]
    {
        let path = PathBuf::from(&home).join(".config/Code/User/mcp.json");
        remove_servers_key(&path)?;
    }

    println!("  ({label}) → Reload VSCode window to deactivate.");
    Ok(())
}

// ── Antigravity ───────────────────────────────────────────────────────────────

fn uninstall_antigravity() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".gemini/antigravity/mcp_config.json");
    remove_mcp_key(&path, "mcpServers")?;

    #[cfg(target_os = "macos")]
    remove_vscode_mcp_entry(&home, "Antigravity/User")?;

    println!("  → Restart Antigravity to deactivate.");
    Ok(())
}

// ── Amazon Q ──────────────────────────────────────────────────────────────────

fn uninstall_amazonq() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".aws/amazonq/mcp.json");
    remove_mcp_key(&path, "mcpServers")?;
    println!("  → Restart Amazon Q to deactivate.");
    Ok(())
}

// ── AWS Kiro ──────────────────────────────────────────────────────────────────

fn uninstall_kiro() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".kiro/settings/mcp.json");
    remove_mcp_key(&path, "mcpServers")?;
    println!("  → Restart Kiro to deactivate.");
    Ok(())
}

// ── OpenAI Codex CLI ──────────────────────────────────────────────────────────

fn uninstall_codex() -> Result<()> {
    let home = super::home_dir();
    let path = PathBuf::from(&home).join(".codex/mcp.json");
    remove_mcp_key(&path, "mcpServers")?;
    println!("  → Restart Codex CLI to deactivate.");
    Ok(())
}

// ── Trae (ByteDance) ──────────────────────────────────────────────────────────

fn uninstall_trae() -> Result<()> {
    #[cfg(target_os = "macos")]
    {
        let home = super::home_dir();
        remove_vscode_mcp_entry(&home, "Trae/User")?;
    }
    println!("  → Restart Trae to deactivate.");
    Ok(())
}

// ── Shell aliases (bash/zsh) ──────────────────────────────────────────────────

fn uninstall_shell(shell: &str) -> Result<()> {
    let home = super::home_dir();
    let candidates: &[&str] = match shell {
        "zsh" => &[".zshrc"],
        "bash" => &[".bashrc", ".bash_profile"],
        _ => unreachable!(),
    };

    for rel in candidates {
        let path = PathBuf::from(&home).join(rel);
        remove_shell_hook(&path)?;
    }
    Ok(())
}

fn remove_shell_hook(path: &Path) -> Result<()> {
    if !path.exists() {
        return Ok(());
    }
    let content = std::fs::read_to_string(path)?;
    if !content.contains("# bctx —") {
        return Ok(());
    }

    // Remove everything from `# bctx —` line through `unset _bctx_cmd` line (inclusive),
    // plus the blank line that init prepended.
    let mut output = String::with_capacity(content.len());
    let mut skip = false;
    for line in content.lines() {
        if line.contains("# bctx —") {
            skip = true;
            // Also drop the preceding blank line that was added by init
            if output.ends_with('\n') {
                output.truncate(output.trim_end_matches('\n').len());
                if !output.is_empty() {
                    output.push('\n');
                }
            }
            continue;
        }
        if skip {
            if line.starts_with("unset _bctx_cmd") || line.starts_with("# bctx end") {
                skip = false;
            }
            continue;
        }
        output.push_str(line);
        output.push('\n');
    }

    std::fs::write(path, &output)?;
    println!("  ✓ {} — bctx hook removed", path.display());
    println!("  → Run: source {}", path.display());
    Ok(())
}

// ── JSON helpers ──────────────────────────────────────────────────────────────

/// Remove `cfg[key]["bctx"]` from a JSON file.
fn remove_mcp_key(path: &Path, key: &str) -> Result<()> {
    if !path.exists() {
        return Ok(());
    }
    let raw = std::fs::read_to_string(path)?;
    let mut cfg: serde_json::Value = serde_json::from_str(&raw).unwrap_or(serde_json::json!({}));
    if cfg[key].get("bctx").is_some() {
        cfg[key]
            .as_object_mut()
            .expect("mcp key is object")
            .remove("bctx");
        std::fs::write(path, serde_json::to_string_pretty(&cfg)?)?;
        println!("  ✓ {} — bctx removed", path.display());
    }
    Ok(())
}

/// Remove `cfg["servers"]["bctx"]` from a VSCode-family mcp.json.
fn remove_servers_key(path: &Path) -> Result<()> {
    remove_mcp_key(path, "servers")
}

/// Remove bctx from a VSCode-family app's mcp.json (macOS only).
#[cfg(target_os = "macos")]
fn remove_vscode_mcp_entry(home: &str, app_subdir: &str) -> Result<()> {
    let path = PathBuf::from(home)
        .join("Library/Application Support")
        .join(app_subdir)
        .join("mcp.json");
    remove_servers_key(&path)
}
