//! 系统提示词构建 + 活跃提供商获取

use df_storage::models::AiProviderRecord;

use crate::commands::err_str;
use crate::state::AppState;

/// 获取当前活跃提供商配置
pub(crate) async fn get_active_provider(state: &AppState) -> Result<AiProviderRecord, String> {
    let session = state.ai_session.lock().await;
    if let Some(ref pid) = session.active_provider_id {
        let provider = state
            .ai_providers
            .get_by_id(pid)
            .await
            .map_err(err_str)?
            .ok_or_else(|| format!("活跃提供商不存在: {}", pid))?;
        Ok(provider)
    } else {
        // 查找默认提供商
        drop(session);
        let providers = state
            .ai_providers
            .list_all()
            .await
            .map_err(err_str)?;
        let default = providers.iter().find(|p| p.is_default).cloned();
        default
            .or_else(|| providers.into_iter().next())
            .ok_or_else(|| "未配置 AI 提供商，请先在设置中添加".to_string())
    }
}

/// 当前运行环境信息（注入 system prompt，供 LLM 写日期/命令时参考）
///
/// - 日期：LLM 无法自行获取当前日期，不注入则写文件日期全靠猜（常从项目已有文件名模式推断出错）
/// - 操作系统：LLM 写 shell 命令需知道平台（dir vs ls、路径分隔符等）
///
/// 两项合计约 25 token，消除一整类系统性错误。
fn env_info_line() -> String {
    let today = chrono::Local::now().format("%Y-%m-%d");
    let os = if cfg!(target_os = "windows") {
        "Windows"
    } else if cfg!(target_os = "macos") {
        "macOS"
    } else if cfg!(target_os = "linux") {
        "Linux"
    } else {
        "Unknown"
    };
    format!("当前日期: {today} | 运行环境: {os}\n\n")
}

/// 按语言返回系统提示词的 (固定前缀, 项目上下文标题, 任务上下文标题)
fn system_prompt_parts(lang: &str) -> (&'static str, &'static str, &'static str) {
    match lang {
        "en" => (
            "You are DevFlow's AI assistant. You help users manage projects, tasks, ideas, and workflows.\n\
             Please respond in English.\n\n\
             ## Capabilities\n\
             You can perform the following actions via tool calls:\n\
             - Create/query projects, tasks, and ideas\n\
             - Run workflows\n\
             - Read file contents, list directories, create/write files\n\
             - Get file metadata (size, line count, binary detection)\n\
             - Append content to files, search files by name pattern\n\n\
             ## Guidelines\n\
             - Briefly explain your intent before executing actions\n\
             - Ask for clarification if the user's intent is unclear\n\
             - Prefer using tools to complete actions rather than just describing steps\n\
             - When a tool call fails, clearly tell the user it failed and why. Never disguise a fallback action as the original intent's success (e.g. don't write to description to fake a directory binding), and never falsely report success\n",
            "\n## Current Projects\n",
            "\n## Current Tasks\n",
        ),
        _ => (
            "你是 DevFlow 的 AI 助手。你帮助用户管理项目、任务、灵感和工作流。\n\
             必须使用简体中文回复，禁止使用繁体中文字符。\n\n\
             ## 当前能力\n\
             你可以通过工具调用执行以下操作：\n\
             - 创建/查询项目、任务、灵感\n\
             - 运行工作流\n\
             - 读取文件内容、列出目录、创建/写入文件\n\
             - 获取文件元信息（大小、行数、二进制检测）\n\
             - 追加写入文件、按名称模式搜索文件\n\n\
             ## 行为准则\n\
             - 执行操作前简要说明你的意图\n\
             - 如果不确定用户意图，先提问\n\
             - 优先使用工具完成操作，而不是只描述步骤\n\
             - 工具调用失败时必须明确告知用户失败原因，严禁用替代操作冒充原意图成功（如绑定目录失败不得改写描述冒充已绑定），也绝不谎报成功\n",
            "\n## 当前项目\n",
            "\n## 当前任务\n",
        ),
    }
}

/// 构建系统提示词（环境信息 + 固定前缀 + 当前项目/任务上下文）
///
/// UX-10 §1.4: 用户在输入框 @ 引用实体时插入 `[类型: 名]` 标记(项目/任务)。
/// 项目上下文已在下方注入,任务上下文本函数新增注入,使 LLM 能解析用户消息中的
/// `[项目: xxx]` / `[任务: xxx]` 标记并对齐到真实实体(名称+状态+描述)。
/// 注入克制:仅各取最近 20 条,防 context 膨胀。
pub(crate) async fn build_system_prompt(state: &AppState, lang: &str) -> String {
    let (prefix, proj_label, task_label) = system_prompt_parts(lang);
    let mut prompt = env_info_line();
    prompt.push_str(prefix);

    // 附加当前数据上下文
    if let Ok(projects) = state.projects.list_active().await {
        if !projects.is_empty() {
            prompt.push_str(proj_label);
            // system prompt 前缀克制：仅最近 20 个项目，防 context 膨胀
            for p in projects.iter().take(20) {
                prompt.push_str(&format!("- {} ({}): {}\n", p.name, p.status, p.description));
            }
        }
    }
    // UX-10 §1.4: 任务上下文(供解析用户消息中 [任务: xxx] 标记)
    // 仅最近 20 条未删除任务,按 created_at DESC(同 list_active 顺序)。
    if let Ok(tasks) = state.tasks.list_active().await {
        if !tasks.is_empty() {
            prompt.push_str(task_label);
            for tk in tasks.iter().take(20) {
                prompt.push_str(&format!("- {} ({}): {}\n", tk.title, tk.status, tk.description));
            }
        }
    }

    prompt
}

/// 上下文压缩 system prompt 模板（F-15 §4.2，纯函数无 IO）。
///
/// 引导 LLM 把对话压缩为四段式结构化摘要（意图/决策/文件/约束），最大化保留
/// 旧消息语义。调用方把返回的 system prompt 放在消息列表首位，后接被压缩的
/// active 消息原文（user/assistant/tool）喂给 `provider.complete()`：
///
/// ```ignore
/// let mut msgs = vec![ChatMessage::system(compress_prompt(lang))];
/// msgs.extend(active_msgs);
/// let resp = provider.complete(CompletionRequest { messages: msgs, .. }).await?;
/// ```
///
/// `lang` 与系统提示词保持一致取值集合（"en" 英文，其余一律中文）。
//
// dead_code:阶段1 基础函数,阶段2 IPC(ai_chat_compress_context)与阶段3 agentic
// 自动压缩会经 compress_via_llm 调用本函数。本阶段只暴露 + 单测,不接调用点。
#[allow(dead_code)]
pub(crate) fn compress_prompt(lang: &str) -> &'static str {
    match lang {
        "en" => "You are a conversation summarizer. Compress the following conversation \
                 into a structured summary that preserves the essential context for \
                 continuing the work. Output exactly four sections:\n\
                 \n\
                 ## Intent\n\
                 The user's core intent and requirements (what they are trying to achieve).\n\
                 \n\
                 ## Decisions\n\
                 Key decisions already made (which approach/option was chosen, why alternatives \
                 were rejected).\n\
                 \n\
                 ## Files\n\
                 Files modified/created/inspected, with a one-line description of the change in each.\n\
                 \n\
                 ## Constraints\n\
                 Important constraints, preferences, or open issues to respect going forward.\n\
                 \n\
                 Rules:\n\
                 - Be concise; prefer bullet points.\n\
                 - Drop small talk and transient pleasantries; keep only technically load-bearing facts.\n\
                 - Preserve file paths, identifiers, and error messages verbatim.\n\
                 - Do NOT invent facts not present in the conversation.\n\
                 - Output the four sections only, no preamble or extra commentary.",
        _ => "你是对话总结器。请把以下对话压缩为结构化摘要,保留继续推进工作所必需的关键上下文。\
               严格按以下四段输出:\n\
               \n\
               ## 意图\n\
               用户的核心意图和需求(用户想要达成什么)。\n\
               \n\
               ## 决策\n\
               已做出的关键决策(选了哪种方案/选项,为何排除其他方案)。\n\
               \n\
               ## 文件\n\
               已修改/创建/查看过的文件,每条一行简述其变更内容。\n\
               \n\
               ## 约束\n\
               重要约束、偏好或待解决的问题,后续需要继续遵守或留意。\n\
               \n\
               要求:\n\
               - 简洁,优先用要点。\n\
               - 去掉寒暄、过渡性客套,只保留技术上有价值的事实。\n\
               - 文件路径、标识符、错误信息等照原样保留。\n\
               - 不要编造对话中没有的事实。\n\
               - 只输出上述四段内容,不要前言、解释或额外评论。",
    }
}
