//! 通用应用设置 KV IPC — 前端 localStorage 迁移目标
//!
//! 4 个 command 读写 `app_settings` 表(key/value JSON 字符串),承载主题、面板折叠态、
//! 最近使用项等前端持久化偏好。返回统一 `Result<T, String>`。

use std::collections::HashMap;

use tauri::State;

use crate::state::AppState;

use super::err_str;

/// 取单个 key 的值(JSON 字符串),不存在返回 None
#[tauri::command]
pub async fn settings_get(
    state: State<'_, AppState>,
    key: String,
) -> Result<Option<String>, String> {
    state.settings.get(&key).await.map_err(err_str)
}

/// 写 key/value(`INSERT OR REPLACE`),刷新 updated_at
#[tauri::command]
pub async fn settings_set(
    state: State<'_, AppState>,
    key: String,
    value: String,
) -> Result<bool, String> {
    state.settings.set(&key, &value).await.map_err(err_str)
}

/// 取全部 key/value(前端启动时一次性拉回恢复偏好)
#[tauri::command]
pub async fn settings_get_all(
    state: State<'_, AppState>,
) -> Result<HashMap<String, String>, String> {
    let rows = state.settings.get_all().await.map_err(err_str)?;
    Ok(rows.into_iter().collect())
}

/// 删除单个 key
#[tauri::command]
pub async fn settings_delete(
    state: State<'_, AppState>,
    key: String,
) -> Result<bool, String> {
    state.settings.delete(&key).await.map_err(err_str)
}

/// 获取 DevFlow 数据目录路径(运行期确定,跨平台)
#[tauri::command]
pub async fn get_data_dir(state: State<'_, AppState>) -> Result<String, String> {
    Ok(state.data_dir.to_string_lossy().to_string())
}

/// 读取审批超时分钟数(默认 15 分钟,0=禁用超时)
///
/// 前端 Settings 页启动时拉取,展示当前配置。
#[tauri::command]
pub async fn ai_get_approval_timeout(state: State<'_, AppState>) -> Result<u64, String> {
    Ok(state.approval_timeout_minutes.load(std::sync::atomic::Ordering::SeqCst))
}

/// 设置审批超时分钟数并持久化(默认 15 分钟,0=禁用超时)
///
/// 前端 Settings 页保存时调用。值范围 0-1440(0=禁用,最大 24 小时)。
/// 注意:此命令与前端 AdvancedSection.vue 的 df-approval-timeout KV 持久化路径不同,
/// 前端走 appSettings.set 走 KV,这里作为后端热改入口。两者最终读写同一个 KV。
#[tauri::command]
pub async fn ai_set_approval_timeout(
    state: State<'_, AppState>,
    minutes: u64,
) -> Result<u64, String> {
    // 范围校验(0-1440 分钟,即 0-24 小时)
    if minutes > 1440 {
        return Err("审批超时分钟数超出范围(最大 1440 = 24 小时)".to_string());
    }
    // 持久化到 Settings KV(与前端同 key,值为毫秒)
    let ms = minutes * 60_000;
    state
        .settings
        .set(
            crate::state::APPROVAL_TIMEOUT_KEY,
            &ms.to_string(),
        )
        .await
        .map_err(err_str)?;
    // 更新内存
    state.approval_timeout_minutes.store(minutes, std::sync::atomic::Ordering::SeqCst);
    tracing::info!("[APPROVAL-TIMEOUT] 已更新: {} 分钟 ({} ms)", minutes, ms);
    Ok(minutes)
}

/// 设置脚本安全配置(白/黑名单)并注入到 ScriptNode 运行时。
///
/// 前端设置页「命令执行安全」保存时调用。
/// 持久化到 app_settings KV,同时通过 df_nodes::set_script_safety_config 注入内存,
/// 即时生效(无需重启)。
#[tauri::command]
pub async fn set_script_safety(
    state: State<'_, AppState>,
    whitelist: String,
    blacklist: String,
) -> Result<(), String> {
    // 持久化到 KV
    state.settings.set("df-script-whitelist", &whitelist).await.map_err(err_str)?;
    state.settings.set("df-script-blacklist", &blacklist).await.map_err(err_str)?;
    // 注入运行时(即时生效,不依赖进程重启)
    df_nodes::script_node::set_script_safety_config(&whitelist, &blacklist);
    tracing::info!(
        whitelist = %whitelist,
        blacklist = %blacklist,
        "[SCRIPT-SAFETY] 运行时配置已更新"
    );
    Ok(())
}

/// 读取脚本安全配置。
#[tauri::command]
pub async fn get_script_safety(
    state: State<'_, AppState>,
) -> Result<(String, String), String> {
    let wl = state.settings.get("df-script-whitelist").await.map_err(err_str)?.unwrap_or_default();
    let bl = state.settings.get("df-script-blacklist").await.map_err(err_str)?.unwrap_or_default();
    Ok((wl, bl))
}

/// 设置多 ReAct Plan 执行开关(默认关)。
/// 开启后 process_tool_calls 内以 JoinSet 并行执行同层工具。
#[tauri::command]
pub async fn set_plan_execution(enabled: bool) -> Result<(), String> {
    df_ai::plan_executor::set_plan_execution(enabled);
    tracing::info!(enabled, "[PLAN-EXEC] IPC 开关已更新");
    Ok(())
}

/// 读取多 ReAct Plan 执行开关状态。
#[tauri::command]
pub async fn get_plan_execution() -> Result<bool, String> {
    Ok(df_ai::plan_executor::plan_execution_enabled())
}

/// 列出可用模板(内置 + 自定义)。
///
/// 内置模板从 assets/templates/ 加载;自定义模板从 app_settings KV(df-custom-templates)读取。
#[tauri::command]
pub async fn list_templates(state: State<'_, AppState>) -> Result<Vec<TemplateInfo>, String> {
    let mut templates = Vec::new();

    // 内置模板
    let builtin = [
        ("code-review", "代码审查", "AI 驱动的代码审查流程"),
        ("bug-fix", "Bug 修复", "AI 驱动的 bug 修复流程"),
        ("feature-dev", "功能开发", "AI 驱动的功能开发全流程"),
    ];
    for (id, name, desc) in &builtin {
        templates.push(TemplateInfo {
            id: id.to_string(),
            name: name.to_string(),
            description: desc.to_string(),
            builtin: true,
            yaml: String::new(), // 内置模板不返回全文(前端需要时单独加载)
        });
    }

    // 自定义模板
    if let Ok(Some(custom_json)) = state.settings.get("df-custom-templates").await {
        if let Ok(custom) = serde_json::from_str::<Vec<TemplateInfo>>(&custom_json) {
            templates.extend(custom);
        }
    }

    Ok(templates)
}

/// 保存自定义模板(新建/覆盖)。
#[tauri::command]
pub async fn save_template(
    state: State<'_, AppState>,
    template: TemplateInfo,
) -> Result<(), String> {
    let mut customs: Vec<TemplateInfo> = state
        .settings
        .get("df-custom-templates")
        .await
        .map_err(err_str)?
        .and_then(|s| serde_json::from_str(&s).ok())
        .unwrap_or_default();

    // 按 id 去重(覆盖同名)
    customs.retain(|t| t.id != template.id);
    customs.push(template);

    let json = serde_json::to_string(&customs).map_err(|e| e.to_string())?;
    state.settings.set("df-custom-templates", &json).await.map_err(err_str)?;
    Ok(())
}

/// 删除自定义模板。
#[tauri::command]
pub async fn delete_template(state: State<'_, AppState>, template_id: String) -> Result<(), String> {
    let mut customs: Vec<TemplateInfo> = state
        .settings
        .get("df-custom-templates")
        .await
        .map_err(err_str)?
        .and_then(|s| serde_json::from_str(&s).ok())
        .unwrap_or_default();

    let before = customs.len();
    customs.retain(|t| t.id != template_id);

    if customs.len() < before {
        let json = serde_json::to_string(&customs).map_err(|e| e.to_string())?;
        state.settings.set("df-custom-templates", &json).await.map_err(err_str)?;
    }
    Ok(())
}

/// 模板信息(IPC 传输用)
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TemplateInfo {
    pub id: String,
    pub name: String,
    pub description: String,
    /// true = 内置模板(只读);false = 自定义模板
    pub builtin: bool,
    /// 自定义模板的 YAML 内容(内置模板为空)
    pub yaml: String,
}

/// 解决合并冲突(用户/reviewer 选择解决方案后调用)。
///
/// 更新 ai_conflicts 表的 resolution + resolved_by + resolved_at,
/// 并 emit AiConflictResolved 事件供前端更新徽章。
#[tauri::command]
pub async fn resolve_conflict(
    state: State<'_, AppState>,
    app_handle: tauri::AppHandle,
    conflict_id: String,
    plan_id: String,
    resolution: String,
    resolved_by: String,
    conversation_id: Option<String>,
) -> Result<(), String> {
    use tauri::Emitter;
    let repo = df_storage::crud::ConflictRepo::new(&state.db);
    let now = df_types::now_millis().to_string();
    let ok = repo.resolve(&conflict_id, &resolution, &resolved_by, &now)
        .await
        .map_err(err_str)?;
    if !ok {
        return Err(format!("冲突 {} 不存在或已解决", conflict_id));
    }
    tracing::info!(
        conflict_id = %conflict_id,
        resolution = %resolution,
        resolved_by = %resolved_by,
        "[CONFLICT] 冲突已解决"
    );
    // emit AiConflictResolved 事件
    let ev = crate::commands::ai::AiChatEvent::AiConflictResolved {
        conflict_id,
        plan_id,
        resolution,
        resolved_by,
        conversation_id,
    };
    let _ = app_handle.emit("ai-chat-event", ev.clone());
    let _ = state.ai_event_bus.publish_event(ev);
    Ok(())
}
