//! skills + config 域 IPC — 5 个 `#[tauri::command]` 函数
//!
//! 原 `commands/ai/commands/mod.rs` 收敛到 re-export only 后,本文件承接最后一批 IPC:
//! - `ai_list_skills` — 列出本机 Claude 技能(skills + commands + plugins),供前端 `/` 联想
//! - `ai_reload_skills` — 热重载技能列表(核心设计6:invalidate + 重扫,不重启生效)
//! - `ai_set_concurrency_config` — LLM 并发上限运行时调整(global/per-conv,立即生效)
//! - `ai_set_agent_max_iterations` — Agentic 循环最大轮次(下次发消息生效)
//! - `ai_set_agent_max_retries` — 流式对话失败自动重试次数(下次发消息生效)
//!
//! re-export 链:本文件经 `commands/ai/commands/mod.rs` 的 `pub use self::config::*`
//! → `commands::*` → `ai/mod.rs` 的 `pub use self::commands::*` 透传到 `commands::ai::*`
//! → `lib.rs` invoke_handler + 前端 `api/ai.ts` 零改动。
//!
//! 注:`ai_list_skills` / `ai_reload_skills` 复用 `super::super::skills::{skills_cached,
//! invalidate_skills, SkillInfo}`(skills.rs 模块提供进程内缓存 + 扫盘逻辑,本文件仅做
//! IPC 包装,不重复实现)。

use std::sync::atomic::Ordering;

use tauri::State;

// skills_cached(进程内缓存命中即 clone,不重复扫盘;RwLock 懒初始化返 owned Vec)
// /SkillInfo(技能统一返回结构)/invalidate_skills(写锁置 None 触发重扫)
// 来自 commands/ai/skills.rs,本域 IPC 复用,不重复定义。
use super::super::skills::{invalidate_skills, skills_cached, SkillInfo};

use crate::state::AppState;

/// 列出本机 Claude 技能（skills + commands + plugins 三类），供前端 `/` 联想
#[tauri::command]
pub async fn ai_list_skills() -> Result<Vec<SkillInfo>, String> {
    // 命中进程内缓存（skills_cached 已 owned Vec，直接返回）
    Ok(skills_cached().await)
}

/// 热重载技能列表（核心设计6:进程内重载，不重启生效）。
///
/// 流程:`invalidate_skills`（写锁置 None）→ `skills_cached`（触发重扫）→ 返回最新列表。
/// 前端在用户改 ~/.claude/skills 后调本 IPC，立即拿到新列表（无需重启 app）。
#[tauri::command]
pub async fn ai_reload_skills() -> Result<Vec<SkillInfo>, String> {
    invalidate_skills();
    Ok(skills_cached().await)
}

/// 设置 LLM 调用并发上限（运行时调整，立即生效）
///
/// 软收敛：缩并发时已持有旧 permit 的任务继续执行不受影响，待其释放后新限制完全生效。
/// None 表示该层不变（前端可单独调一层）。值下限为 1。
#[tauri::command]
pub async fn ai_set_concurrency_config(
    state: State<'_, AppState>,
    global_limit: Option<u32>,
    per_conv_limit: Option<u32>,
) -> Result<(), String> {
    // 下限 1,无上限;同时给 global 时约束 per-conv 不超过 global
    if let Some(g) = global_limit {
        state.llm_concurrency.set_global(g.max(1) as usize).await;
    }
    if let Some(p) = per_conv_limit {
        let mut p = p.max(1);
        if let Some(g) = global_limit {
            p = p.min(g.max(1));
        }
        state.llm_concurrency.set_per_conv(p as usize).await;
    }
    Ok(())
}

/// 设置 Agentic 循环最大轮次（运行时调整，立即生效）
///
/// 与并发配置不同：max_iterations 是 loop 入口 load 快照的值，热改后当前 loop 不受影响
/// （已锁定边界），下次发消息生效。范围双 clamp（command 端 1-50 + 前端 input min/max），
/// 防越界输入致 loop 过早结束（值过小）或失控（值过大）。
#[tauri::command]
pub async fn ai_set_agent_max_iterations(
    state: State<'_, AppState>,
    value: u32,
) -> Result<(), String> {
    // clamp 0-50：0 = 不限(run_agentic_loop 内 0→usize::MAX,loop 靠 stop/收敛/审批退出,不触发达上限暂停);
    // 1-50 正常上限,上界 50 防失控烧 token(50 轮足够覆盖复杂多步任务)
    let clamped = value.clamp(0, 50) as usize;
    state.agent_max_iterations.store(clamped, Ordering::SeqCst);
    Ok(())
}

/// 设置流式对话失败自动重试次数（F-260616-07 / 决策 a1：运行时调整，下次发消息生效）
///
/// 只重试流前失败（Init Err：未输出任何 token）；流中途失败（MidStream Partial）保文不重试。
/// 退避复用 retry::backoff_delay(1s→2s→4s+jitter) + is_status_retryable Fatal 分类 +
/// 30s 总预算(详见 agentic/mod.rs 流前重试循环)。
/// 范围 clamp 0-10：0 表示不重试（直接报错），上限 10 防过度重试烧 token/拖慢体验。
/// 默认 3（复用 retry.rs backoff_delay + 错误分类，详见 agentic/mod.rs 重试循环）。
#[tauri::command]
pub async fn ai_set_agent_max_retries(
    state: State<'_, AppState>,
    value: u32,
) -> Result<(), String> {
    let clamped = value.clamp(0, 10) as usize;
    state.agent_max_retries.store(clamped, Ordering::SeqCst);
    Ok(())
}

// ============================================================
// F-260619-03 Phase A: AI 工具文件访问授权目录白名单 IPC
//
// 持久化到 app_settings KV key=`allowed_dirs`(JSON 字符串数组),
// 内存 AppState.allowed_dirs 同步。workspace_root 始终在白名单(向后兼容)。
// resolve_workspace_path_with_allowed 校验:文件工具(read/write/list/patch/...)
// 仅可访问授权目录。Phase A 仅持久化,不挂起弹窗(Phase B)。
// ============================================================

/// 获取 AI 工具授权目录列表(含 workspace_root,规范化字符串)。
#[tauri::command]
pub async fn ai_get_allowed_dirs(state: State<'_, AppState>) -> Result<Vec<String>, String> {
    Ok(state.get_allowed_dirs().await)
}

/// 设置 AI 工具授权目录列表(持久化 Settings KV + 同步内存白名单)。
///
/// 传入目录字符串数组(去空去重);后端 canonicalize 规范化后持久化。
/// workspace_root 始终隐式在白名单(is_authorized 内置),无需前端显式传入。
/// 返回持久化后内存白名单的规范化字符串列表(供前端回显)。
#[tauri::command]
pub async fn ai_set_allowed_dirs(
    state: State<'_, AppState>,
    dirs: Vec<String>,
) -> Result<Vec<String>, String> {
    state.set_allowed_dirs(dirs).await.map_err(|e| e.to_string())
}
