use super::{db, query};
use rusqlite::Connection;
use serde_json::{json, Value};
use std::io::{BufRead, Write};
use std::path::Path;

/// 取符号源码时单文件最大读取上限，超过则不读
const SOURCE_READ_LIMIT: u64 = 4 * 1024 * 1024;

const PROTOCOL_VERSION: &str = "2024-11-05";
const SERVER_NAME: &str = "ccgui-index";

/// initialize 时回给客户端（Claude Code）的服务器使用说明。MCP 客户端会把它并入
/// 模型上下文，引导 Agent 在本项目中优先走索引、并理解各工具的适用场景。
const INSTRUCTIONS: &str = "本服务器（ccgui-index）提供当前项目源码的预建符号索引，\
覆盖函数/类/方法/结构体/接口/trait 等定义、引用、调用关系、类型层级、导入依赖与全文检索。\
在本项目中定位代码、理解结构时，应优先使用这些工具，而非逐个文件 grep 或通读：\
按名查定义用 search_symbols、goto_definition 或 get_symbol_source（后者直接返回源码片段，省去再读文件）；\
追引用与调用关系用 find_references、call_hierarchy；看继承与实现用 type_hierarchy；\
查导入与反向依赖用 module_graph、who_imports；\
搜字符串字面量、注释、配置项等符号覆盖不到的内容用 search_text。\
结果基于语法标记而非完整语义解析，带 confidence 时高置信优先；\
若结果为空或疑有盲区，可用 index_status 查看索引覆盖，再回退到常规文本搜索。";

/// MCP stdio 服务器入口（--mcp-index 模式）：只读打开该项目的索引库，
/// 按行读取 stdin 的 JSON-RPC 请求并回应。客户端关闭 stdin（EOF）即退出。
pub fn run_stdio(project_root: String) {
    let db_path = db::db_path_for(&project_root);
    let conn = db::open_ro(&db_path);
    let stdin = std::io::stdin();
    let root = project_root.as_str();
    let mut stdout = std::io::stdout();
    let mut lock = stdin.lock();
    let mut buf = String::new();
    loop {
        buf.clear();
        match lock.read_line(&mut buf) {
            Ok(0) | Err(_) => break, // EOF 或读错误
            Ok(_) => {}
        }
        let line = buf.trim();
        if line.is_empty() {
            continue;
        }
        let req: Value = match serde_json::from_str(line) {
            Ok(v) => v,
            Err(_) => continue,
        };
        if let Some(resp) = handle(&req, conn.as_ref(), root) {
            if let Ok(s) = serde_json::to_string(&resp) {
                let _ = writeln!(stdout, "{s}");
                let _ = stdout.flush();
            }
        }
    }
}

fn handle(req: &Value, conn: Option<&Connection>, root: &str) -> Option<Value> {
    let id = req.get("id").cloned();
    let method = req.get("method").and_then(|m| m.as_str()).unwrap_or("");
    match method {
        "initialize" => {
            let pv = req
                .get("params")
                .and_then(|p| p.get("protocolVersion"))
                .and_then(|v| v.as_str())
                .unwrap_or(PROTOCOL_VERSION);
            Some(result(
                id,
                json!({
                    "protocolVersion": pv,
                    "capabilities": { "tools": {} },
                    "serverInfo": { "name": SERVER_NAME, "version": env!("CARGO_PKG_VERSION") },
                    "instructions": INSTRUCTIONS
                }),
            ))
        }
        "tools/list" => Some(result(id, json!({ "tools": tool_defs() }))),
        "tools/call" => Some(handle_call(id, req, conn, root)),
        "ping" => Some(result(id, json!({}))),
        "shutdown" => Some(result(id, Value::Null)),
        _ => {
            // 通知（无 id）不回应；未知请求方法回 method not found
            if id.is_none() {
                None
            } else {
                Some(error(id, -32601, "Method not found"))
            }
        }
    }
}

fn handle_call(id: Option<Value>, req: &Value, conn: Option<&Connection>, root: &str) -> Value {
    let params = req.get("params");
    let name = params
        .and_then(|p| p.get("name"))
        .and_then(|v| v.as_str())
        .unwrap_or("");
    let args = params
        .and_then(|p| p.get("arguments"))
        .cloned()
        .unwrap_or_else(|| json!({}));

    let conn = match conn {
        Some(c) => c,
        None => {
            return tool_text(
                id,
                "索引尚未建立：请在 CCGUI 中为该项目开启代码索引并等待首次扫描完成。",
            )
        }
    };

    let outcome: Result<Value, String> = match name {
        "search_symbols" => {
            let q = args.get("query").and_then(|v| v.as_str()).unwrap_or("");
            let kind = args.get("kind").and_then(|v| v.as_str());
            let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(30) as u32;
            query::search_symbols(conn, q, kind, limit).map(|r| json!(r))
        }
        "goto_definition" => {
            let n = args.get("name").and_then(|v| v.as_str()).unwrap_or("");
            let kind = args.get("kind").and_then(|v| v.as_str());
            query::goto_definition(conn, n, kind).map(|r| json!(r))
        }
        "file_outline" => {
            let p = args.get("path").and_then(|v| v.as_str()).unwrap_or("");
            query::file_outline(conn, p).map(|r| json!(r))
        }
        "find_references" => {
            let n = args.get("name").and_then(|v| v.as_str()).unwrap_or("");
            let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(50) as u32;
            query::find_references(conn, n, limit).map(|r| json!(r))
        }
        "module_graph" => {
            let p = args.get("path").and_then(|v| v.as_str());
            query::module_graph(conn, p).map(|r| json!(r))
        }
        "who_imports" => {
            let p = args.get("path").and_then(|v| v.as_str()).unwrap_or("");
            query::who_imports(conn, p).map(|r| json!(r))
        }
        "search_text" => {
            let q = args.get("query").and_then(|v| v.as_str()).unwrap_or("");
            let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(50) as u32;
            query::search_text(conn, q, limit).map(|r| json!(r))
        }
        "index_status" => query::index_summary(conn).map(|r| json!(r)),
        "call_hierarchy" => {
            let n = args.get("name").and_then(|v| v.as_str()).unwrap_or("");
            query::call_hierarchy(conn, n).map(|r| json!(r))
        }
        "type_hierarchy" => {
            let n = args.get("name").and_then(|v| v.as_str()).unwrap_or("");
            query::type_hierarchy(conn, n).map(|r| json!(r))
        }
        "get_symbol_source" => {
            let n = args.get("name").and_then(|v| v.as_str()).unwrap_or("");
            let kind = args.get("kind").and_then(|v| v.as_str());
            let path = args.get("path").and_then(|v| v.as_str());
            get_symbol_source(conn, root, n, kind, path)
        }
        _ => Err(format!("未知工具: {name}")),
    };

    match outcome {
        Ok(v) => {
            let text = serde_json::to_string_pretty(&v).unwrap_or_else(|_| "[]".to_string());
            tool_text(id, &text)
        }
        Err(e) => json!({
            "jsonrpc": "2.0",
            "id": id.unwrap_or(Value::Null),
            "result": { "content": [{ "type": "text", "text": e }], "isError": true }
        }),
    }
}

/// 定位符号定义并直接返回其源码片段（省去 Agent 再 Read 一次文件）
fn get_symbol_source(
    conn: &Connection,
    root: &str,
    name: &str,
    kind: Option<&str>,
    path_filter: Option<&str>,
) -> Result<Value, String> {
    let mut defs = query::goto_definition(conn, name, kind)?;
    if let Some(pf) = path_filter {
        let pf = pf.trim_start_matches("./").replace('\\', "/");
        defs.retain(|d| d.file == pf || d.file.ends_with(&format!("/{pf}")));
    }
    if defs.is_empty() {
        return Ok(json!({ "error": format!("未找到定义: {name}") }));
    }
    let mut out = Vec::new();
    for d in defs.iter().take(20) {
        let abs = Path::new(root).join(&d.file);
        let source = read_line_range(&abs, d.line, d.end_line);
        out.push(json!({
            "file": d.file,
            "startLine": d.line,
            "endLine": d.end_line,
            "kind": d.kind,
            "source": source,
        }));
    }
    Ok(json!(out))
}

/// 读取文件的 [start, end]（1 基，含端点）行；越界/过大/读失败返回提示串而非 panic
fn read_line_range(path: &Path, start: u32, end: u32) -> String {
    let meta = match std::fs::metadata(path) {
        Ok(m) => m,
        Err(_) => return "<源文件不存在或无法访问>".to_string(),
    };
    if meta.len() > SOURCE_READ_LIMIT {
        return "<源文件过大，未读取>".to_string();
    }
    let content = match std::fs::read_to_string(path) {
        Ok(c) => c,
        Err(_) => return "<源文件非文本或读取失败>".to_string(),
    };
    let start = start.max(1) as usize;
    let end = end.max(start as u32) as usize;
    let lines: Vec<&str> = content
        .lines()
        .skip(start - 1)
        .take(end - start + 1)
        .collect();
    if lines.is_empty() {
        "<行号超出文件范围>".to_string()
    } else {
        lines.join("\n")
    }
}

fn tool_defs() -> Value {
    json!([
        {
            "name": "search_symbols",
            "description": "模糊搜索代码符号定义（函数/类/方法/结构体/接口等）。比 grep 更精确，适合按名查找定义所在。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "query": { "type": "string", "description": "符号名或片段" },
                    "kind": { "type": "string", "description": "可选，按类型过滤，如 function/class/method/struct" },
                    "limit": { "type": "integer", "description": "返回上限，默认 30" }
                },
                "required": ["query"]
            }
        },
        {
            "name": "goto_definition",
            "description": "按精确名称定位符号定义位置（可能多处）。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "name": { "type": "string", "description": "精确符号名" },
                    "kind": { "type": "string", "description": "可选类型过滤" }
                },
                "required": ["name"]
            }
        },
        {
            "name": "file_outline",
            "description": "列出指定文件内的所有符号定义（大纲）。path 可为项目相对路径或文件名后缀。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "path": { "type": "string", "description": "文件相对路径，如 src/index/mod.rs" }
                },
                "required": ["path"]
            }
        },
        {
            "name": "find_references",
            "description": "查找某符号名的引用/调用出现处（基于语法标记，非完整语义解析）。每条带 confidence：high=引用所在文件确实导入了定义文件，low=仅名字匹配；高置信优先。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "name": { "type": "string", "description": "符号名" },
                    "limit": { "type": "integer", "description": "返回上限，默认 50" }
                },
                "required": ["name"]
            }
        },
        {
            "name": "module_graph",
            "description": "查看文件的导入/依赖关系。给 path 看单文件，省略则列出所有有导入的文件。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "path": { "type": "string", "description": "可选，文件相对路径" }
                }
            }
        },
        {
            "name": "search_text",
            "description": "全文检索源码行：能搜到字符串字面量、注释、TODO、配置 key 等符号搜索覆盖不到的内容（trigram，关键词≥3字符）。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "query": { "type": "string", "description": "检索关键词（≥3 字符）" },
                    "limit": { "type": "integer", "description": "返回上限，默认 50" }
                },
                "required": ["query"]
            }
        },
        {
            "name": "index_status",
            "description": "索引状态：已索引文件数、符号定义数、上次扫描时间(ms)、解析失败文件清单（判断索引覆盖与盲区）。",
            "inputSchema": { "type": "object", "properties": {} }
        },
        {
            "name": "call_hierarchy",
            "description": "调用层级：callers=谁调用了该符号（带置信度），callees=该函数体内调用到的符号。基于语法标记，非完整语义解析。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "name": { "type": "string", "description": "函数/方法名" }
                },
                "required": ["name"]
            }
        },
        {
            "name": "type_hierarchy",
            "description": "类型层级：supertypes=该类型继承/实现的父类型，subtypes=继承/实现该类型的子类型（含 Rust trait impl）。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "name": { "type": "string", "description": "类/接口/trait 名" }
                },
                "required": ["name"]
            }
        },
        {
            "name": "who_imports",
            "description": "反向依赖：列出导入了指定文件的所有文件（改某文件波及谁）。需要导入已解析。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "path": { "type": "string", "description": "目标文件相对路径或后缀，如 src/stores/auth.ts" }
                },
                "required": ["path"]
            }
        },
        {
            "name": "get_symbol_source",
            "description": "定位符号定义并直接返回其源码片段（含文件名与起止行），省去再单独读文件。",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "name": { "type": "string", "description": "精确符号名" },
                    "kind": { "type": "string", "description": "可选类型过滤" },
                    "path": { "type": "string", "description": "可选，限定文件（相对路径或后缀）" }
                },
                "required": ["name"]
            }
        }
    ])
}

fn result(id: Option<Value>, result: Value) -> Value {
    json!({ "jsonrpc": "2.0", "id": id.unwrap_or(Value::Null), "result": result })
}

fn error(id: Option<Value>, code: i64, msg: &str) -> Value {
    json!({ "jsonrpc": "2.0", "id": id.unwrap_or(Value::Null), "error": { "code": code, "message": msg } })
}

fn tool_text(id: Option<Value>, text: &str) -> Value {
    json!({
        "jsonrpc": "2.0",
        "id": id.unwrap_or(Value::Null),
        "result": { "content": [{ "type": "text", "text": text }], "isError": false }
    })
}
