//! API 注入：管理 Claude Code 全局模型接口（~/.claude/settings.json 的 env 与顶层 model）
//!
//! - 自定义接口（名称/地址/Key/模型）持久化在 %APPDATA%/ccgui/api-endpoints.json
//! - 「注入」把选中接口写入 settings.json：env.ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN /
//!   ANTHROPIC_MODEL + 顶层 model
//! - 「重置」无条件删除管理的 env 键与顶层 model，恢复 Claude Code 官方原生接口

use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value};
use std::fs;
use std::path::{Path, PathBuf};
use tauri::AppHandle;

use crate::quota::ModelInfo;

/// 本工具管理的 settings.json env 键（重置时全部清理）
const MANAGED_ENV_KEYS: [&str; 5] = [
    "ANTHROPIC_BASE_URL",
    "ANTHROPIC_AUTH_TOKEN",
    "ANTHROPIC_API_KEY",
    "ANTHROPIC_MODEL",
    "CLAUDE_CODE_SUBAGENT_MODEL",
];

/// 内置虚拟接口 id：多模型路由（注入后 settings.json 指向本地代理）
pub const ROUTER_ENDPOINT_ID: &str = "builtin:router";

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ApiEndpoint {
    /// 空串表示新建（保存时生成）
    pub id: String,
    pub name: String,
    pub base_url: String,
    pub key: String,
    /// 首选模型：直连注入时写入 settings（顶层 model + env.ANTHROPIC_MODEL）
    pub model: String,
    /// 该接口参与路由的模型清单（路由模式聚合用）；空 = 不参与路由
    #[serde(default)]
    pub models: Vec<String>,
}

/// 路由表条目：对外模型名 → 某接口的真实模型名
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct RouteRule {
    pub id: String,
    /// 对外模型名（claude 侧可见；全局唯一；禁用 CLI 保留别名 sonnet/opus/haiku/fable）
    pub alias: String,
    pub endpoint_id: String,
    /// 转发时改写进请求体的真实模型名
    pub model: String,
}

/// 路由代理配置
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RouterConfig {
    /// 首选端口；被占用时系统分配并回写
    pub port: u16,
    /// 占位鉴权 token（首次启用路由时生成）
    #[serde(default)]
    pub token: String,
    /// 默认接口：未命中路由的 messages + 非 messages 路径透传到它
    #[serde(default)]
    pub default_endpoint_id: String,
    /// 主模型 alias：路由注入时写入 settings（env.ANTHROPIC_MODEL，实测优先于顶层 model）
    #[serde(default)]
    pub main_alias: String,
    /// subagent 兜底模型：非空则写 env.CLAUDE_CODE_SUBAGENT_MODEL（优先级高于 frontmatter）
    #[serde(default)]
    pub subagent_model: String,
}

impl Default for RouterConfig {
    fn default() -> Self {
        Self {
            port: 45123,
            token: String::new(),
            default_endpoint_id: String::new(),
            main_alias: String::new(),
            subagent_model: String::new(),
        }
    }
}

/// 注入状态：记录当前注入来源（列表"使用中"高亮）。重置一律恢复官方原生，无需快照
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct InjectionState {
    pub active: bool,
    pub endpoint_id: String,
}

#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ApiInjectStore {
    #[serde(default)]
    endpoints: Vec<ApiEndpoint>,
    #[serde(default)]
    injection: InjectionState,
    #[serde(default)]
    routes: Vec<RouteRule>,
    #[serde(default)]
    router: RouterConfig,
}

/// 当前全局接口状态（前端状态卡片）
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiStatus {
    /// 自定义接口地址；None = 官方默认
    pub base_url: Option<String>,
    /// Key 掩码（前6…后4）；None = 未设置
    pub key_masked: Option<String>,
    /// 当前生效的模型（env.ANTHROPIC_MODEL 优先——实测 T3，其次顶层 model）
    pub model: Option<String>,
    /// 未配置任何自定义接口/Key
    pub is_official: bool,
    /// 本工具记录的注入来源（用于列表高亮"使用中"）
    pub injected_endpoint_id: Option<String>,
    /// 三态：official（官方）/ direct（直连某接口）/ router（多模型路由代理）
    pub mode: String,
}

// ---------- 存储（%APPDATA%/ccgui/api-endpoints.json） ----------

fn store_path() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("."))
        .join("ccgui")
        .join("api-endpoints.json")
}

fn load_store() -> ApiInjectStore {
    load_store_at(&store_path())
}

fn load_store_at(path: &Path) -> ApiInjectStore {
    fs::read_to_string(path)
        .ok()
        .and_then(|raw| serde_json::from_str(&raw).ok())
        .unwrap_or_default()
}

fn save_store(store: &ApiInjectStore) -> Result<(), String> {
    save_store_at(&store_path(), store)
}

fn save_store_at(path: &Path, store: &ApiInjectStore) -> Result<(), String> {
    let raw = serde_json::to_string_pretty(store).map_err(|e| e.to_string())?;
    crate::fsx::write_atomic(path, raw.as_bytes())
}

// ---------- settings.json 读写 ----------

fn read_settings() -> Result<Value, String> {
    read_settings_at(&crate::hooks::settings_path())
}

/// 读取 settings.json；文件不存在返回空对象，解析失败报错（绝不覆盖用户文件）
fn read_settings_at(path: &Path) -> Result<Value, String> {
    match fs::read_to_string(path) {
        Ok(raw) => {
            let v: Value = serde_json::from_str(&raw)
                .map_err(|e| format!("settings.json 解析失败，请手动修复后再操作: {e}"))?;
            if !v.is_object() {
                return Err("settings.json 顶层不是对象，请手动修复后再操作".into());
            }
            Ok(v)
        }
        Err(_) => Ok(json!({})),
    }
}

fn write_settings_at(path: &Path, root: &Value) -> Result<(), String> {
    let raw = serde_json::to_string_pretty(root).map_err(|e| e.to_string())?;
    crate::fsx::write_atomic(path, raw.as_bytes())
}

fn env_str(root: &Value, key: &str) -> Option<String> {
    root.get("env")?
        .get(key)?
        .as_str()
        .filter(|s| !s.is_empty())
        .map(str::to_string)
}

/// 可写的 env 对象；不存在则创建，存在但不是对象则报错
fn env_mut<'a>(root: &'a mut Value) -> Result<&'a mut Map<String, Value>, String> {
    let env = root
        .as_object_mut()
        .unwrap()
        .entry("env")
        .or_insert_with(|| json!({}));
    if !env.is_object() {
        return Err("settings.json 中 env 字段不是对象，请手动修复后再操作".into());
    }
    Ok(env.as_object_mut().unwrap())
}

fn mask_key(key: &str) -> String {
    let chars: Vec<char> = key.chars().collect();
    if chars.len() > 10 {
        let head: String = chars[..6].iter().collect();
        let tail: String = chars[chars.len() - 4..].iter().collect();
        format!("{head}…{tail}")
    } else {
        "****".into()
    }
}

// ---------- 业务逻辑 ----------

pub fn status() -> Result<ApiStatus, String> {
    let root = read_settings()?;
    let store = load_store();
    let base_url = env_str(&root, "ANTHROPIC_BASE_URL");
    let key = env_str(&root, "ANTHROPIC_AUTH_TOKEN").or_else(|| env_str(&root, "ANTHROPIC_API_KEY"));
    // 实测（T3）：env.ANTHROPIC_MODEL 优先于顶层 model 字段
    let model = env_str(&root, "ANTHROPIC_MODEL").or_else(|| {
        root.get("model")
            .and_then(Value::as_str)
            .filter(|s| !s.is_empty())
            .map(str::to_string)
    });
    let is_router = base_url
        .as_deref()
        .map(|u| u.starts_with("http://127.0.0.1:") || u.starts_with("http://localhost:"))
        .unwrap_or(false);
    Ok(ApiStatus {
        is_official: base_url.is_none() && key.is_none(),
        mode: if is_router {
            "router".into()
        } else if base_url.is_some() || key.is_some() {
            "direct".into()
        } else {
            "official".into()
        },
        base_url,
        key_masked: key.as_deref().map(mask_key),
        model,
        injected_endpoint_id: if store.injection.active && !store.injection.endpoint_id.is_empty() {
            Some(store.injection.endpoint_id)
        } else {
            None
        },
    })
}

pub fn list_endpoints() -> Vec<ApiEndpoint> {
    load_store().endpoints
}

pub fn save_endpoint(mut endpoint: ApiEndpoint) -> Result<Vec<ApiEndpoint>, String> {
    endpoint.name = endpoint.name.trim().into();
    endpoint.base_url = endpoint.base_url.trim().trim_end_matches('/').into();
    endpoint.key = endpoint.key.trim().into();
    endpoint.model = endpoint.model.trim().into();
    // 路由模型清单清洗：trim、去空、去重
    let mut seen = std::collections::HashSet::new();
    endpoint.models = endpoint
        .models
        .into_iter()
        .map(|m| m.trim().to_string())
        .filter(|m| !m.is_empty())
        .filter(|m| seen.insert(m.clone()))
        .collect();
    if endpoint.name.is_empty() {
        return Err("请填写接口名称".into());
    }
    if !endpoint.base_url.starts_with("http://") && !endpoint.base_url.starts_with("https://") {
        return Err("接口地址需以 http:// 或 https:// 开头".into());
    }
    if endpoint.key.is_empty() {
        return Err("请填写 API Key".into());
    }
    let mut store = load_store();
    if endpoint.id.is_empty() {
        endpoint.id = uuid::Uuid::new_v4().simple().to_string();
        store.endpoints.push(endpoint);
    } else {
        let Some(existing) = store.endpoints.iter_mut().find(|e| e.id == endpoint.id) else {
            return Err("接口不存在，可能已被删除".into());
        };
        *existing = endpoint;
    }
    save_store(&store)?;
    Ok(store.endpoints)
}

pub fn delete_endpoint(id: &str) -> Result<Vec<ApiEndpoint>, String> {
    let mut store = load_store();
    store.endpoints.retain(|e| e.id != id);
    // 已注入的接口被删除：注入值仍在 settings.json 中保持有效，仅去掉"使用中"标记
    if store.injection.endpoint_id == id {
        store.injection.endpoint_id.clear();
    }
    save_store(&store)?;
    Ok(store.endpoints)
}

/// 拉取自定义接口的模型列表：GET {base}/v1/models
/// 同时携带 Bearer 与 x-api-key 两种鉴权头，兼容 Anthropic 原生与 OpenAI 风格中转
pub fn fetch_models(base_url: &str, key: &str) -> Result<Vec<ModelInfo>, String> {
    let base = base_url.trim().trim_end_matches('/');
    if base.is_empty() {
        return Err("接口地址不能为空".into());
    }
    let key = key.trim();
    // 地址末尾已含 /v1 时只补 /models，避免拼出 /v1/v1/models
    let url = if base.ends_with("/v1") {
        format!("{base}/models?limit=100")
    } else {
        format!("{base}/v1/models?limit=100")
    };
    let resp = ureq::get(&url)
        .set("Authorization", &format!("Bearer {key}"))
        .set("x-api-key", key)
        .set("anthropic-version", "2023-06-01")
        .set("Accept", "application/json")
        .timeout(std::time::Duration::from_secs(15))
        .call();
    let body: Value = match resp {
        Ok(r) => r.into_json().map_err(|e| format!("响应解析失败: {e}"))?,
        Err(ureq::Error::Status(code, r)) => {
            let text = r.into_string().unwrap_or_default();
            let snippet: String = text.chars().take(200).collect();
            return Err(format!("接口错误 HTTP {code}: {snippet}"));
        }
        Err(e) => return Err(format!("网络错误: {e}")),
    };
    let models = body
        .get("data")
        .and_then(Value::as_array)
        .map(|arr| {
            arr.iter()
                .filter_map(|m| {
                    let id = m.get("id").and_then(Value::as_str)?;
                    let display_name = m
                        .get("display_name")
                        .and_then(Value::as_str)
                        .unwrap_or(id);
                    Some(ModelInfo {
                        id: id.to_string(),
                        display_name: display_name.to_string(),
                    })
                })
                .collect::<Vec<_>>()
        })
        .unwrap_or_default();
    if models.is_empty() {
        return Err("接口未返回任何模型（响应格式可能不兼容），请手动填写模型 ID".into());
    }
    Ok(models)
}

/// 注入：把选中接口写入 settings.json（env + 顶层 model）
/// id 为 ROUTER_ENDPOINT_ID 时走路由分支：settings 指向本地代理
pub fn apply(id: &str) -> Result<(), String> {
    apply_at(id, &store_path(), &crate::hooks::settings_path())
}

/// 路由分支的 settings 写入（纯函数，便于测试）：
/// base_url 指向本地代理 + 占位 token + 主模型 alias（env 为准，顶层 model 兜底）+ 按需 subagent 模型
fn apply_router_settings(
    root: &mut Value,
    port: u16,
    token: &str,
    main_alias: &str,
    subagent_model: &str,
) -> Result<(), String> {
    {
        let env = env_mut(root)?;
        env.insert(
            "ANTHROPIC_BASE_URL".into(),
            json!(format!("http://127.0.0.1:{port}")),
        );
        env.insert("ANTHROPIC_AUTH_TOKEN".into(), json!(token));
        env.remove("ANTHROPIC_API_KEY");
        env.insert("ANTHROPIC_MODEL".into(), json!(main_alias));
        if subagent_model.is_empty() {
            env.remove("CLAUDE_CODE_SUBAGENT_MODEL");
        } else {
            env.insert("CLAUDE_CODE_SUBAGENT_MODEL".into(), json!(subagent_model));
        }
    }
    root.as_object_mut().unwrap().insert("model".into(), json!(main_alias));
    Ok(())
}

/// 路由注入前置：校验配置 → 补 token → 确保代理运行 → 回写实际端口。返回 (端口, token)
fn ensure_router(store: &mut ApiInjectStore, store_p: &Path) -> Result<(u16, String), String> {
    if store.router.default_endpoint_id.is_empty()
        || !store
            .endpoints
            .iter()
            .any(|e| e.id == store.router.default_endpoint_id)
    {
        return Err("请先在路由配置里选择一个有效的默认接口".into());
    }
    if store.router.main_alias.is_empty() {
        return Err("请先在路由配置里选择主模型".into());
    }
    if store.router.token.is_empty() {
        store.router.token = uuid::Uuid::new_v4().simple().to_string();
    }
    let actual = crate::router_proxy::ensure_running(store.router.port, router_snapshot(store))?;
    if actual != store.router.port {
        store.router.port = actual;
    }
    save_store_at(store_p, store)?;
    Ok((actual, store.router.token.clone()))
}

/// 从 store 构造代理运行时快照
pub(crate) fn router_snapshot(store: &ApiInjectStore) -> crate::router_proxy::RouterSnapshot {
    crate::router_proxy::RouterSnapshot {
        token: store.router.token.clone(),
        routes: store.routes.clone(),
        endpoints: store.endpoints.clone(),
        default_endpoint_id: store.router.default_endpoint_id.clone(),
    }
}

fn apply_at(id: &str, store_p: &Path, settings_p: &Path) -> Result<(), String> {
    let mut store = load_store_at(store_p);
    let mut root = read_settings_at(settings_p)?;

    // 路由分支：settings 指向本地代理
    if id == ROUTER_ENDPOINT_ID {
        let (port, token) = ensure_router(&mut store, store_p)?;
        let main = store.router.main_alias.clone();
        let sub = store.router.subagent_model.clone();
        apply_router_settings(&mut root, port, &token, &main, &sub)?;
        store.injection = InjectionState {
            active: true,
            endpoint_id: ROUTER_ENDPOINT_ID.into(),
        };
        write_settings_at(settings_p, &root)?;
        save_store_at(store_p, &store)?;
        return Ok(());
    }

    let endpoint = store
        .endpoints
        .iter()
        .find(|e| e.id == id)
        .cloned()
        .ok_or_else(|| "接口不存在，可能已被删除".to_string())?;

    store.injection = InjectionState {
        active: true,
        endpoint_id: endpoint.id.clone(),
    };

    {
        let env = env_mut(&mut root)?;
        env.insert(
            "ANTHROPIC_BASE_URL".into(),
            json!(endpoint.base_url.trim_end_matches('/')),
        );
        env.insert("ANTHROPIC_AUTH_TOKEN".into(), json!(endpoint.key));
        // 避免旧 API_KEY 与 AUTH_TOKEN 并存造成鉴权歧义
        env.remove("ANTHROPIC_API_KEY");
        // 直连模式不写 subagent 模型（属路由模式配置）
        env.remove("CLAUDE_CODE_SUBAGENT_MODEL");
        if endpoint.model.is_empty() {
            env.remove("ANTHROPIC_MODEL");
        } else {
            env.insert("ANTHROPIC_MODEL".into(), json!(endpoint.model));
        }
    }
    let obj = root.as_object_mut().unwrap();
    if endpoint.model.is_empty() {
        obj.remove("model");
    } else {
        obj.insert("model".into(), json!(endpoint.model));
    }
    write_settings_at(settings_p, &root)?;
    save_store_at(store_p, &store)
}

/// 重置：无条件删除管理的 env 键与顶层 model，恢复官方原生接口
pub fn reset() -> Result<(), String> {
    reset_at(&store_path(), &crate::hooks::settings_path())
}

fn reset_at(store_p: &Path, settings_p: &Path) -> Result<(), String> {
    let mut store = load_store_at(store_p);
    let mut root = read_settings_at(settings_p)?;

    if let Some(env) = root.get_mut("env").and_then(Value::as_object_mut) {
        for key in MANAGED_ENV_KEYS {
            env.remove(key);
        }
    }
    root.as_object_mut().unwrap().remove("model");
    // env 空对象则整体移除，保持文件干净
    let env_empty = root
        .get("env")
        .and_then(Value::as_object)
        .map(|e| e.is_empty())
        .unwrap_or(false);
    if env_empty {
        root.as_object_mut().unwrap().remove("env");
    }
    write_settings_at(settings_p, &root)?;
    store.injection = InjectionState::default();
    save_store_at(store_p, &store)
}

// ---------- 路由配置（多模型路由代理） ----------

/// 路由面板一次性视图：配置 + 路由表 + 接口列表 + 代理运行状态
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RouterView {
    config: RouterConfig,
    routes: Vec<RouteRule>,
    endpoints: Vec<ApiEndpoint>,
    proxy: crate::router_proxy::RouterStatus,
}

/// CCGUI 启动时恢复：若 settings 处于路由注入态，自动拉起代理（否则已注入的 claude 会话全挂）
pub fn ensure_router_on_boot() {
    let Ok(root) = read_settings() else { return };
    let is_router = env_str(&root, "ANTHROPIC_BASE_URL")
        .map(|u| u.starts_with("http://127.0.0.1:") || u.starts_with("http://localhost:"))
        .unwrap_or(false);
    if !is_router {
        return;
    }
    let mut store = load_store();
    let Ok(actual) = crate::router_proxy::ensure_running(store.router.port, router_snapshot(&store))
    else {
        return;
    };
    // 首选端口被占用而改绑了新端口：回写 store 与 settings，保持 base_url 与实际端口一致
    if actual != store.router.port {
        store.router.port = actual;
        let _ = save_store(&store);
        let mut root = root;
        if let Ok(env) = env_mut(&mut root) {
            env.insert(
                "ANTHROPIC_BASE_URL".into(),
                json!(format!("http://127.0.0.1:{actual}")),
            );
            let _ = write_settings_at(&crate::hooks::settings_path(), &root);
        }
    }
}

/// 保存路由配置：校验 → 落盘 → 热更新代理快照；若当前为路由注入态，同步重写 settings
fn save_router(config: RouterConfig, routes: Vec<RouteRule>) -> Result<(), String> {
    let mut store = load_store();
    // 校验路由表
    let mut aliases = std::collections::HashSet::new();
    for r in &routes {
        let alias = r.alias.trim();
        if alias.is_empty() {
            return Err("路由条目存在空的对外模型名".into());
        }
        if alias.chars().any(char::is_whitespace) {
            return Err(format!(
                "对外模型名「{alias}」不能包含空白字符（/model 命令按空格分词，含空格会被截断）"
            ));
        }
        if crate::router_proxy::is_reserved_alias(alias) {
            return Err(format!(
                "「{alias}」是 Claude Code 保留别名（会被 CLI 抢先解析），请换一个自定义名称"
            ));
        }
        if r.model.trim().is_empty() {
            return Err(format!("路由条目「{alias}」未填写真实模型名"));
        }
        if !store.endpoints.iter().any(|e| e.id == r.endpoint_id) {
            return Err(format!("路由条目「{alias}」指向的接口不存在"));
        }
        if !aliases.insert(alias.to_string()) {
            return Err(format!("对外模型名「{alias}」重复"));
        }
    }
    if !config.default_endpoint_id.is_empty()
        && !store
            .endpoints
            .iter()
            .any(|e| e.id == config.default_endpoint_id)
    {
        return Err("默认接口不存在".into());
    }
    store.router = RouterConfig {
        // token 由系统生成/持有，不随前端提交改动
        token: store.router.token,
        ..config
    };
    store.routes = routes;
    save_store(&store)?;
    crate::router_proxy::update_snapshot(router_snapshot(&store));

    // 当前是路由注入态：主模型/subagent 模型等改动立即重写到 settings（对新会话生效）
    if store.injection.active && store.injection.endpoint_id == ROUTER_ENDPOINT_ID {
        let mut root = read_settings()?;
        apply_router_settings(
            &mut root,
            store.router.port,
            &store.router.token,
            &store.router.main_alias,
            &store.router.subagent_model,
        )?;
        write_settings_at(&crate::hooks::settings_path(), &root)?;
    }
    Ok(())
}

/// 当前注入态下的可选模型清单（供新建对话框与对话页模型菜单）：
/// router → 本地聚合（路由 alias + 各接口实名模型，不发网络请求）；
/// direct → 用 settings 里的 base_url/key 实时拉取 /v1/models
pub fn active_models() -> Result<Vec<ModelInfo>, String> {
    let root = read_settings()?;
    let base_url = env_str(&root, "ANTHROPIC_BASE_URL");
    let is_router = base_url
        .as_deref()
        .map(|u| u.starts_with("http://127.0.0.1:") || u.starts_with("http://localhost:"))
        .unwrap_or(false);
    if is_router {
        let store = load_store();
        let agg = crate::router_proxy::aggregate_models(&router_snapshot(&store));
        let models = agg["data"]
            .as_array()
            .map(|arr| {
                arr.iter()
                    .filter_map(|m| {
                        let id = m["id"].as_str()?.to_string();
                        let display = m["display_name"].as_str().unwrap_or(&id).to_string();
                        Some(ModelInfo {
                            id,
                            display_name: display,
                        })
                    })
                    .collect()
            })
            .unwrap_or_default();
        return Ok(models);
    }
    let base = base_url.ok_or("当前为官方模式，无自定义接口清单")?;
    let key = env_str(&root, "ANTHROPIC_AUTH_TOKEN")
        .or_else(|| env_str(&root, "ANTHROPIC_API_KEY"))
        .unwrap_or_default();
    fetch_models(&base, &key)
}

// ---------- Tauri commands ----------

#[tauri::command]
pub fn api_inject_status() -> Result<ApiStatus, String> {
    status()
}

#[tauri::command]
pub fn api_inject_list() -> Vec<ApiEndpoint> {
    list_endpoints()
}

#[tauri::command]
pub fn api_inject_save(endpoint: ApiEndpoint) -> Result<Vec<ApiEndpoint>, String> {
    save_endpoint(endpoint)
}

#[tauri::command]
pub fn api_inject_delete(id: String) -> Result<Vec<ApiEndpoint>, String> {
    delete_endpoint(&id)
}

// 标注 (async)：网络请求 /v1/models，移出主线程
#[tauri::command(async)]
pub fn api_inject_fetch_models(base_url: String, key: String) -> Result<Vec<ModelInfo>, String> {
    fetch_models(&base_url, &key)
}

#[tauri::command]
pub fn api_inject_apply(id: String) -> Result<(), String> {
    apply(&id)
}

#[tauri::command]
pub fn api_inject_reset() -> Result<(), String> {
    reset()
}

// 标注 (async)：direct 分支有网络请求，移出主线程
#[tauri::command(async)]
pub fn api_inject_active_models() -> Result<Vec<ModelInfo>, String> {
    active_models()
}

#[tauri::command]
pub fn api_inject_router_get() -> RouterView {
    let store = load_store();
    RouterView {
        config: store.router.clone(),
        routes: store.routes.clone(),
        endpoints: store.endpoints.clone(),
        proxy: crate::router_proxy::status(),
    }
}

#[tauri::command]
pub fn api_inject_router_save(config: RouterConfig, routes: Vec<RouteRule>) -> Result<(), String> {
    save_router(config, routes)
}

#[tauri::command]
pub fn router_status() -> crate::router_proxy::RouterStatus {
    crate::router_proxy::status()
}

#[tauri::command]
pub fn router_logs() -> Vec<crate::router_proxy::RequestLog> {
    crate::router_proxy::logs()
}

/// 立即重启应用（注入/重置成功后提示重启生效）
#[tauri::command]
pub fn restart_app(app: AppHandle) {
    app.restart();
}

#[cfg(test)]
mod tests {
    use super::*;

    /// 每个用例独立的临时目录，返回 (store 路径, settings 路径)
    fn temp_paths(tag: &str) -> (PathBuf, PathBuf) {
        let dir = std::env::temp_dir().join(format!(
            "ccgui-api-inject-test-{}-{tag}",
            std::process::id()
        ));
        let _ = fs::remove_dir_all(&dir);
        fs::create_dir_all(&dir).unwrap();
        (dir.join("store.json"), dir.join("settings.json"))
    }

    fn endpoint(id: &str) -> ApiEndpoint {
        ApiEndpoint {
            id: id.into(),
            name: format!("测试接口{id}"),
            base_url: format!("https://api-{id}.example.com"),
            key: format!("sk-key-{id}-123456"),
            model: format!("claude-model-{id}"),
            models: vec![],
        }
    }

    fn seed_store(store_p: &Path, endpoints: Vec<ApiEndpoint>) {
        save_store_at(
            store_p,
            &ApiInjectStore {
                endpoints,
                injection: InjectionState::default(),
                routes: vec![],
                router: RouterConfig::default(),
            },
        )
        .unwrap();
    }

    fn read_json(path: &Path) -> Value {
        serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap()
    }

    /// 官方原样 → 注入 → 重置：settings.json 应逐字节回到注入前
    #[test]
    fn inject_then_reset_restores_official() {
        let (store_p, settings_p) = temp_paths("official");
        fs::write(&settings_p, r#"{"hooks": {"UserPromptSubmit": [1]}}"#).unwrap();
        seed_store(&store_p, vec![endpoint("a")]);

        apply_at("a", &store_p, &settings_p).unwrap();
        let injected = read_json(&settings_p);
        assert_eq!(injected["env"]["ANTHROPIC_BASE_URL"], "https://api-a.example.com");
        assert_eq!(injected["env"]["ANTHROPIC_AUTH_TOKEN"], "sk-key-a-123456");
        assert_eq!(injected["env"]["ANTHROPIC_MODEL"], "claude-model-a");
        assert_eq!(injected["model"], "claude-model-a");
        // 无关字段原样保留
        assert_eq!(injected["hooks"]["UserPromptSubmit"], json!([1]));

        reset_at(&store_p, &settings_p).unwrap();
        let restored = read_json(&settings_p);
        assert!(restored.get("env").is_none(), "env 应整体移除: {restored}");
        assert!(restored.get("model").is_none(), "model 应移除: {restored}");
        assert_eq!(restored["hooks"]["UserPromptSubmit"], json!([1]));
    }

    /// 注入前已有手动自定义配置：重置同样清掉（无条件恢复官方原生），仅保留无关字段
    #[test]
    fn reset_wipes_preexisting_custom_values() {
        let (store_p, settings_p) = temp_paths("preexisting");
        fs::write(
            &settings_p,
            r#"{"env": {"ANTHROPIC_BASE_URL": "https://old.example.com", "ANTHROPIC_AUTH_TOKEN": "old-key", "OTHER": "keep"}, "model": "opus"}"#,
        )
        .unwrap();
        seed_store(&store_p, vec![endpoint("b")]);

        apply_at("b", &store_p, &settings_p).unwrap();
        reset_at(&store_p, &settings_p).unwrap();

        let restored = read_json(&settings_p);
        let env = restored["env"].as_object().unwrap();
        assert!(env.get("ANTHROPIC_BASE_URL").is_none());
        assert!(env.get("ANTHROPIC_AUTH_TOKEN").is_none());
        assert!(env.get("ANTHROPIC_API_KEY").is_none());
        assert!(env.get("ANTHROPIC_MODEL").is_none());
        assert_eq!(env["OTHER"], "keep");
        assert!(restored.get("model").is_none());
    }

    /// 无快照（用户手动配置的接口）：重置应删除管理键与顶层 model，回到官方
    #[test]
    fn reset_without_snapshot_clears_managed_keys_and_model() {
        let (store_p, settings_p) = temp_paths("nosnapshot");
        fs::write(
            &settings_p,
            r#"{"env": {"ANTHROPIC_BASE_URL": "https://manual.example.com", "ANTHROPIC_AUTH_TOKEN": "mk", "ANTHROPIC_API_KEY": "ak", "ANTHROPIC_MODEL": "m0", "OTHER": "keep"}, "model": "m1"}"#,
        )
        .unwrap();
        // store 文件不存在 = 无注入快照
        reset_at(&store_p, &settings_p).unwrap();

        let restored = read_json(&settings_p);
        let env = restored["env"].as_object().unwrap();
        assert!(env.get("ANTHROPIC_BASE_URL").is_none());
        assert!(env.get("ANTHROPIC_AUTH_TOKEN").is_none());
        assert!(env.get("ANTHROPIC_API_KEY").is_none());
        assert!(env.get("ANTHROPIC_MODEL").is_none());
        assert_eq!(env["OTHER"], "keep");
        assert!(restored.get("model").is_none(), "顶层 model 也应移除: {restored}");
    }

    /// A → B 连续注入后重置：回到官方原生
    #[test]
    fn switch_endpoint_then_reset_restores_official() {
        let (store_p, settings_p) = temp_paths("switch");
        fs::write(&settings_p, r#"{}"#).unwrap();
        seed_store(&store_p, vec![endpoint("a"), endpoint("b")]);

        apply_at("a", &store_p, &settings_p).unwrap();
        apply_at("b", &store_p, &settings_p).unwrap();
        let injected = read_json(&settings_p);
        assert_eq!(injected["env"]["ANTHROPIC_BASE_URL"], "https://api-b.example.com");

        reset_at(&store_p, &settings_p).unwrap();
        let restored = read_json(&settings_p);
        assert!(restored.get("env").is_none(), "应回到官方: {restored}");
        assert!(restored.get("model").is_none());
    }

    /// 路由注入的 settings 写入：指向本地代理 + 占位 token + 主模型（env 与顶层 model 同写）+ subagent 模型
    #[test]
    fn router_settings_point_to_local_proxy() {
        let mut root = json!({"env": {"ANTHROPIC_API_KEY": "ak", "OTHER": "keep"}});
        apply_router_settings(&mut root, 45123, "tok-123", "glm-cheap", "ds-fast").unwrap();

        assert_eq!(root["env"]["ANTHROPIC_BASE_URL"], "http://127.0.0.1:45123");
        assert_eq!(root["env"]["ANTHROPIC_AUTH_TOKEN"], "tok-123");
        assert!(root["env"].get("ANTHROPIC_API_KEY").is_none(), "旧 API_KEY 应清除");
        assert_eq!(root["env"]["ANTHROPIC_MODEL"], "glm-cheap");
        assert_eq!(root["env"]["CLAUDE_CODE_SUBAGENT_MODEL"], "ds-fast");
        assert_eq!(root["model"], "glm-cheap");
        assert_eq!(root["env"]["OTHER"], "keep", "无关字段保留");

        // subagent 模型留空：键应移除而非写空串
        apply_router_settings(&mut root, 45123, "tok-123", "glm-cheap", "").unwrap();
        assert!(root["env"].get("CLAUDE_CODE_SUBAGENT_MODEL").is_none());
    }
}
