/**
 * ============================================================
 *  src/tools/websearch.ts —— WebSearch 工具（联网搜索）
 * ------------------------------------------------------------
 *  底层调用 Tavily Search API（https://tavily.com/）。
 *
 *  为什么选 Tavily：
 *    - LLM 友好：返回结构化 JSON，自带 content 摘要；不用我们再去抓 HTML
 *    - 接口简单：一个 POST + JSON body 搞定，不用 OAuth / 翻墙
 *    - 免费额度：每月 1000 次足够学习用
 *
 *  Key 配置：
 *    在项目根 .env 里加：
 *      TAVILY_API_KEY=tvly-xxxxxxxxxxxxxxxx
 *    没设的话，工具会返回友好错误（不影响 agent 启动）。
 *
 *  输入：
 *    query         必填，搜索关键词
 *    max_results   可选，默认 5（Tavily 上限 20）
 *    search_depth  可选 'basic' | 'advanced'，默认 basic
 *    topic         可选 'general' | 'news'，默认 general
 *
 *  输出：
 *    每条结果一段：
 *      [1] 标题
 *      URL: https://...
 *      内容摘要（content）
 *    如有 answer 字段（advanced 才返回），先列在最上面。
 * ============================================================
 */
import { z } from 'zod';
import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
import { toToolParameters } from '../../utils/zodToJson.js';
// ---------------- 1. Zod 输入 schema ----------------
const inputSchema = z.object({
    query: z
        .string()
        .min(1, '必须提供搜索关键词')
        .max(400, '搜索关键词太长（>400 字）')
        .describe('搜索关键词，建议自然语言描述需要查的信息'),
    max_results: z
        .number()
        .int()
        .min(1)
        .max(20)
        .optional()
        .describe('返回结果数量，1-20，默认 5'),
    search_depth: z
        .enum(['basic', 'advanced'])
        .optional()
        .describe('basic 快但浅；advanced 慢但深（含 answer 摘要），默认 basic'),
    topic: z
        .enum(['general', 'news'])
        .optional()
        .describe('general=通用网页；news=偏新闻源；默认 general'),
});
// ---------------- 2. JSON Schema（由 Zod 自动派生） ----------------
const parameters = toToolParameters(inputSchema);
// ---------------- 3. Description（给 LLM 看的工具说明） ----------------
const description = `- Searches the public web via the Tavily Search API and returns structured results.
- Use this when you need up-to-date information that is not in your training data, or when the user asks for recent news / docs / API references.
- Returns the top N results, each with a title, URL, and content snippet. With \`search_depth: "advanced"\` Tavily also returns a synthesized answer at the top.
- Prefer specific natural-language queries over keyword soup (e.g. "how does Bun handle .env files in version 1.1").
- Requires the TAVILY_API_KEY environment variable to be set; if missing the tool returns a friendly error.`;
// ---------------- 5. call() ----------------
async function call(input, signal) {
    // 5a. 取 key
    const apiKey = process.env.TAVILY_API_KEY;
    if (!apiKey) {
        return {
            ok: false,
            error: 'WebSearch 不可用：环境变量 TAVILY_API_KEY 未设置。\n' +
                '请在项目根 .env 里加：\n' +
                '  TAVILY_API_KEY=tvly-你的key\n' +
                '免费 key 申请：https://tavily.com/',
        };
    }
    // 5b. 自动追加当前日期到 query（强制时间限制，防止搜索到过时信息）
    const today = new Date().toISOString().slice(0, 10);
    const queryWithDate = input.query.includes(today)
        ? input.query
        : `${input.query} (as of ${today})`;
    const body = {
        api_key: apiKey,
        query: queryWithDate,
        max_results: input.max_results ?? 5,
        search_depth: input.search_depth ?? 'basic',
        topic: input.topic ?? 'general',
        // 让 advanced 模式自动给 answer；basic 不会有
        include_answer: input.search_depth === 'advanced',
    };
    // 5c. 发请求
    let res;
    try {
        res = await fetch('https://api.tavily.com/search', {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify(body),
            signal,
        });
    }
    catch (e) {
        return {
            ok: false,
            error: `调用 Tavily 失败（网络）：${e.message}`,
        };
    }
    if (!res.ok) {
        const text = await res.text().catch(() => '');
        return {
            ok: false,
            error: `Tavily HTTP ${res.status}：${text.slice(0, 500)}`,
        };
    }
    // 5d. 解析
    let data;
    try {
        data = (await res.json());
    }
    catch (e) {
        return {
            ok: false,
            error: `Tavily 响应不是合法 JSON：${e.message}`,
        };
    }
    const results = data.results ?? [];
    if (results.length === 0) {
        return {
            ok: true,
            content: `(no results for "${input.query}")`,
        };
    }
    // 5e. 拼输出
    const parts = [];
    if (data.answer) {
        parts.push(`【综合回答】\n${data.answer}\n`);
    }
    parts.push(`【共 ${results.length} 条结果】`);
    results.forEach((r, i) => {
        const date = r.published_date ? `（${r.published_date}）` : '';
        parts.push(`\n[${i + 1}] ${r.title}${date}\nURL: ${r.url}\n${(r.content ?? '').trim()}`);
    });
    let content = parts.join('\n');
    if (content.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
        content =
            content.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS) +
                `\n\n... (输出超过 ${DEFAULT_MAX_RESULT_SIZE_CHARS} 字符，已截断)`;
    }
    return { ok: true, content };
}
// ---------------- 6. 导出 ----------------
export const webSearchTool = {
    name: 'WebSearch',
    description,
    inputSchema,
    parameters,
    isReadOnly: true,
    isConcurrencySafe: true,
    maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
    call,
};
