//! 桌面进程内嵌 HTTP MCP server。
//!
//! 复用 AppState.db 与 GUI 共享同一数据库连接,在 tauri 后台任务里常驻监听
//! 127.0.0.1:<port>/mcp,多 Claude 会话 HTTP 直连同一桌面进程读写 DevFlow 数据。
//!
//! 运行时状态(McpStatus)以 `tauri::State<Mutex<McpStatus>>` 管理:
//! - setup 阶段 manage 初始 `{ running: false, port }`
//! - spawn 内 bind 成功/失败/异常退出时更新 running
//! - 前端经 `mcp_get_status` IPC 查询(Mutex 保证跨线程一致)
use std::sync::{Arc, Mutex};

use serde::Serialize;
use tauri::{AppHandle, Manager, State};

use df_mcp::server_http::{self, McpHttpState};
use df_storage::db::Database;

/// 前端可查的 MCP 状态。
#[derive(Clone, Serialize)]
pub struct McpStatus {
    /// server 是否正在监听(bind 成功为 true;绑定失败/异常退出为 false)
    pub running: bool,
    /// 实际监听端口(env `DEVFLOW_MCP_PORT` 覆盖,默认 18765)
    pub port: u16,
}

/// managed state 别名:跨线程共享的 MCP 运行时状态。
/// setup 中 `app.manage(Mutex::new(McpStatus { running: false, port }))` 注入,
/// spawn 内更新、`mcp_get_status` 读取。
pub type McpStatusState = Mutex<McpStatus>;

/// 解析 MCP 监听端口:优先 env `DEVFLOW_MCP_PORT`,否则默认 18765。
/// 供 setup(spawn 前 manage 初始状态)与 mcp_get_status(兜底)共用,保证一致。
pub fn mcp_port() -> u16 {
    std::env::var("DEVFLOW_MCP_PORT")
        .ok()
        .and_then(|p| p.parse::<u16>().ok())
        .unwrap_or(server_http::DEFAULT_MCP_PORT)
}

/// 在 tauri 后台任务里启动 HTTP MCP server(复用 AppState.db,与 GUI 共享连接)。
///
/// 前置条件:setup 中已 `app.manage(Mutex::new(McpStatus{...}))`(本函数内只更新不注入)。
/// bind 成功/失败/异常退出都会同步更新 managed 状态,前端 mcp_get_status 可查实时值。
pub fn spawn_mcp_http(app: &AppHandle, db: Arc<Database>) {
    // 工具调用回调:复用现有 df-data-changed 机制,让 GUI 前端在 MCP 写库后自动刷新。
    // 非变更类工具(如 list/get)在 emit_data_changed 内部映射不到即 noop,零副作用。
    let app_for_cb = app.clone();
    let on_tool_call: Option<Arc<dyn Fn(&str) + Send + Sync>> = Some(Arc::new(move |tool_name: &str| {
        crate::commands::ai::audit::emit_data_changed(&app_for_cb, tool_name);
    }));
    let state = McpHttpState::new(db, false, on_tool_call);
    let port = mcp_port();
    // 后台 task 持有 app clone,用于 bind 结果写回 managed 状态
    let app_for_status = app.clone();
    tauri::async_runtime::spawn(async move {
        let listener = match tokio::net::TcpListener::bind(("127.0.0.1", port)).await {
            Ok(l) => l,
            Err(e) => {
                tracing::error!(port, err = %e, "[mcp-http] 绑定端口失败, MCP server 不可用(可设 DEVFLOW_MCP_PORT 换端口)");
                set_mcp_running(&app_for_status, false);
                return;
            }
        };
        // 绑定成功,更新状态为 running(供前端查询)
        set_mcp_running(&app_for_status, true);
        tracing::info!(port, "[mcp-http] MCP server listening on 127.0.0.1:{port}/mcp");
        if let Err(e) = server_http::serve_on(listener, state).await {
            tracing::error!(err = %e, "[mcp-http] server 异常退出");
            set_mcp_running(&app_for_status, false);
        }
    });
}

/// 更新 managed McpStatus.running。state 未注入/try_state 失败/锁中毒时静默跳过
/// (状态保持原值,非致命,防 panic 拖垮后台任务)。
fn set_mcp_running(app: &AppHandle, running: bool) {
    if let Some(state) = app.try_state::<McpStatusState>() {
        if let Ok(mut guard) = state.lock() {
            guard.running = running;
        }
    }
}

/// IPC command:前端查询 MCP server 状态。
#[tauri::command]
pub fn mcp_get_status(state: State<'_, McpStatusState>) -> McpStatus {
    match state.lock() {
        Ok(guard) => McpStatus {
            running: guard.running,
            port: guard.port,
        },
        // 锁中毒(理论上不应发生):降级返回静态配置,防 panic
        Err(_) => McpStatus {
            running: false,
            port: mcp_port(),
        },
    }
}
