"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildToolsetManualTools = buildToolsetManualTools;
exports.renderToolsetManualContent = renderToolsetManualContent;
exports.formatToolsetManualIntro = formatToolsetManualIntro;
exports.buildManTool = buildManTool;
const work_language_1 = require("../runtime/work-language");
const team_1 = require("../team");
const tool_1 = require("../tool");
const output_limit_1 = require("./manual/output-limit");
const render_1 = require("./manual/render");
const spec_1 = require("./manual/spec");
const registry_1 = require("./registry");
const team_mgmt_manual_1 = require("./team_mgmt-manual");
function buildToolsetManualTools(_options) {
    // Create a single unified `man` function instead of man_xxx series
    const tool = buildManTool();
    if (_options.existingToolNames.has(tool.name)) {
        return { tools: [], toolNames: [] };
    }
    return { tools: [tool], toolNames: [tool.name] };
}
async function renderToolsetManualContent(input) {
    if (input.toolsetId === 'team_mgmt') {
        return renderManualResult(input.language, input.toolsetId, await renderTeamMgmtGuideViaManualRequest(input.language, {
            topic: input.topic,
            topics: input.topics,
        }));
    }
    const rendered = (0, render_1.renderToolsetManual)({
        toolsetId: input.toolsetId,
        language: input.language,
        request: {
            requestedTopic: input.topic,
            requestedTopics: input.topics,
        },
    });
    if (!rendered.foundToolset) {
        return renderManualResult(input.language, input.toolsetId, input.language === 'zh'
            ? `未找到 toolset '${input.toolsetId}'。可用的 toolsets: ${Object.keys((0, registry_1.listToolsets)()).join(', ')}`
            : `Toolset '${input.toolsetId}' not found. Available toolsets: ${Object.keys((0, registry_1.listToolsets)()).join(', ')}`);
    }
    return renderManualResult(input.language, input.toolsetId, rendered.content);
}
function renderManualResult(language, toolsetId, content) {
    if (content.length <= output_limit_1.MANUAL_SINGLE_REQUEST_CHAR_LIMIT) {
        return content;
    }
    return language === 'zh'
        ? [
            '# Tool Manual',
            '',
            `当前请求的 \`${toolsetId}\` 手册内容过长（${content.length} chars），本次不直接截断正文，以免说明语义残缺。`,
            '请按业务章节缩小范围后再读，例如只请求一个 topic，或改用更少的 topics。',
            `示例：\`man({ "toolsetId": "${toolsetId}", "topic": "tools" })\``,
            `示例：\`man({ "toolsetId": "${toolsetId}", "topics": ["principles","errors"] })\``,
        ].join('\n')
        : [
            '# Tool Manual',
            '',
            `The requested manual content for \`${toolsetId}\` is too large (${content.length} chars). This tool does not hard-cut the prose body because that would damage the manual's meaning.`,
            'Narrow the request by business section instead: ask for one topic, or a smaller set of topics.',
            `Example: \`man({ "toolsetId": "${toolsetId}", "topic": "tools" })\``,
            `Example: \`man({ "toolsetId": "${toolsetId}", "topics": ["principles","errors"] })\``,
        ].join('\n');
}
function getToolsetDescription(language, toolsetId) {
    const meta = (0, registry_1.getToolsetMeta)(toolsetId);
    const description = meta?.descriptionI18n;
    if (!description) {
        return language === 'zh' ? '工具集说明暂缺' : 'No description available';
    }
    return description[language] ?? description.en ?? description.zh ?? '';
}
function formatToolsetEntry(language, toolsetId, includeCall) {
    const description = getToolsetDescription(language, toolsetId);
    const endsWithSentencePunctuation = description.endsWith('。') ||
        description.endsWith('.') ||
        description.endsWith('!') ||
        description.endsWith('！');
    if (language === 'zh') {
        return includeCall
            ? `- \`${toolsetId}\`：${description}${endsWithSentencePunctuation ? '' : '。'}查看详情：\`man({ "toolsetId": "${toolsetId}" })\``
            : `- \`${toolsetId}\`：${description}`;
    }
    return includeCall
        ? `- \`${toolsetId}\`: ${description}${endsWithSentencePunctuation ? '' : '.'} Details: \`man({ "toolsetId": "${toolsetId}" })\``
        : `- \`${toolsetId}\`: ${description}`;
}
function formatToolsetEntries(language, toolsetIds, includeCall) {
    if (toolsetIds.length === 0) {
        return language === 'zh' ? '（当前无可用工具集）' : '(no toolsets currently available)';
    }
    return toolsetIds
        .map((toolsetId) => formatToolsetEntry(language, toolsetId, includeCall))
        .join('\n');
}
function formatToolsetIdList(language, toolsetIds) {
    if (toolsetIds.length === 0) {
        return language === 'zh' ? '（当前无可用工具集）' : '(no toolsets currently available)';
    }
    return toolsetIds.map((toolsetId) => `- \`${toolsetId}\``).join('\n');
}
function formatToolsetManualIntro(language, toolNames, toolsetIds) {
    const names = toolNames.map((name) => `\`${name}\``).join(', ');
    if (toolNames.length === 0) {
        return language === 'zh' ? '（无可用手册工具）' : '(no manual tool available)';
    }
    if (language === 'zh') {
        return [
            `手册工具：${names}`,
            '可用工具集：',
            formatToolsetEntries(language, toolsetIds, true),
            '何时查阅手册：当某个工具集的整体定位、使用边界、典型场景、安全规范或错误处理不确定时，调用 `man` 查看详情。',
        ].join('\n');
    }
    return [
        `Manual tool: ${names}`,
        'Available toolsets:',
        formatToolsetEntries(language, toolsetIds, true),
        'When to read the manual: call `man` when a toolset’s positioning, usage boundaries, typical scenarios, guardrails, or error handling are unclear.',
    ].join('\n');
}
function buildManTool() {
    const topicEnums = [...spec_1.MANUAL_TOPICS, 'all'];
    return {
        type: 'func',
        name: 'man',
        description: `Show manual for a toolset. Use without 'toolsetId' to list available toolsets. Use 'toolsetId' to inspect one toolset and 'topic' or 'topics' to select sections.`,
        descriptionI18n: {
            en: `Show manual for a toolset. Use without 'toolsetId' to list available toolsets. Use 'toolsetId' to inspect one toolset and 'topic' or 'topics' to select sections.`,
            zh: `查看工具集手册。不带 'toolsetId' 时列出可用工具集；带上 'toolsetId' 时查看单个工具集；使用 'topic' 或 'topics' 选择章节。`,
        },
        parameters: {
            type: 'object',
            additionalProperties: false,
            properties: {
                toolsetId: {
                    type: 'string',
                    description: 'The toolset ID to show manual for. Use without toolsetId to get available toolsets.',
                },
                topic: {
                    type: 'string',
                    description: `Single topic to display. Standard topics: ${topicEnums.join(', ')}. Some toolsets may accept additional toolset-specific topic keys.`,
                },
                topics: {
                    type: 'array',
                    items: {
                        type: 'string',
                    },
                    description: 'Multiple topics to display (union). Standard topics are index/principles/tools/scenarios/errors; some toolsets may accept additional toolset-specific topic keys.',
                },
            },
        },
        argsValidation: 'dominds',
        async call(_dlg, _caller, args) {
            const language = typeof _dlg?.getLastUserLanguageCode === 'function'
                ? _dlg.getLastUserLanguageCode()
                : (0, work_language_1.getWorkLanguage)();
            const dynamicToolsetNames = await team_1.Team.listDynamicToolsetNamesForMember({
                member: _caller,
                taskDocPath: _dlg.taskDocPath,
            });
            const declaredMcpToolsets = await team_1.Team.readMcpDeclaredToolsets();
            const declaredMcpToolsetNames = declaredMcpToolsets.kind === 'loaded' ? declaredMcpToolsets.declaredServerIds : undefined;
            const invalidMcpToolsetNames = declaredMcpToolsets.kind === 'loaded' ? declaredMcpToolsets.invalidServerIds : undefined;
            // Get toolsetId from args
            const toolsetId = args?.toolsetId;
            if (!toolsetId) {
                // When no toolsetId is provided, show all available toolsets
                const availableToolsetNames = _caller.listResolvedToolsetNames({
                    dynamicToolsetNames,
                    declaredMcpToolsetNames,
                    invalidMcpToolsetNames,
                });
                if (language === 'zh') {
                    return (0, tool_1.toolSuccess)([
                        '**可用工具集**',
                        '',
                        formatToolsetIdList(language, availableToolsetNames),
                        '',
                        '提示：当某个工具集的整体定位、使用边界、场景示例、安全规范或错误处理不确定时，继续调用 `man({ "toolsetId": "<toolset>" })` 查看详情。',
                    ].join('\n'));
                }
                return (0, tool_1.toolSuccess)([
                    '**Available toolsets**',
                    '',
                    formatToolsetIdList(language, availableToolsetNames),
                    '',
                    'Hint: when a toolset’s positioning, usage boundaries, scenarios, guardrails, or error handling are unclear, call `man({ "toolsetId": "<toolset>" })` for details.',
                ].join('\n'));
            }
            // Step 1: Get available toolsets for this caller (dynamic availability)
            const availableToolsetNames = _caller.listResolvedToolsetNames({
                dynamicToolsetNames,
                declaredMcpToolsetNames,
                invalidMcpToolsetNames,
            });
            // Find closest match for fuzzy matching
            const allToolsets = Object.keys((0, registry_1.listToolsets)());
            const suggestion = findClosestToolset(toolsetId, allToolsets);
            // Step 2: Check if the toolset is available for this caller
            if (!availableToolsetNames.includes(toolsetId)) {
                // Toolset is not available for this user
                if (suggestion && availableToolsetNames.includes(suggestion)) {
                    // The suggested toolset IS available, user might have meant that
                    return (0, tool_1.toolSuccess)(language === 'zh'
                        ? `工具集 '${toolsetId}' 暂未配置给您使用。您是否在找：'${suggestion}'？`
                        : `Toolset '${toolsetId}' is not available to you. Did you mean: '${suggestion}'?`);
                }
                // No suggestion available, just report unavailability and list available toolsets
                const list = formatToolsetIdList(language, availableToolsetNames);
                return (0, tool_1.toolSuccess)(language === 'zh'
                    ? `工具集 '${toolsetId}' 暂未配置给您使用。\n\n可用工具集：\n${list}`
                    : `Toolset '${toolsetId}' is not available to you.\n\nAvailable toolsets:\n${list}`);
            }
            if (toolsetId === 'team_mgmt') {
                return (0, tool_1.toolSuccess)(renderManualResult(language, toolsetId, await renderTeamMgmtGuideViaMan(language, args)));
            }
            return (0, tool_1.toolSuccess)(await renderToolsetManualContent({
                toolsetId,
                language,
                topic: typeof args?.topic === 'string' ? args.topic : undefined,
                topics: Array.isArray(args?.topics)
                    ? args.topics.filter((entry) => typeof entry === 'string')
                    : undefined,
            }));
        },
    };
}
const TEAM_MGMT_GUIDE_COMPAT_TOPIC_MAP = {
    index: ['topics'],
    principles: ['team', 'permissions', 'llm', 'mcp', 'minds', 'skills', 'priming', 'env'],
    tools: ['team', 'toolsets', 'llm', 'mcp'],
    scenarios: ['topics', 'team', 'minds', 'skills', 'priming'],
    errors: ['troubleshooting'],
};
function normalizeTeamMgmtGuideTopics(args) {
    const out = [];
    const seen = new Set();
    const push = (value) => {
        const trimmed = value.trim();
        if (trimmed === '' || seen.has(trimmed))
            return;
        seen.add(trimmed);
        out.push(trimmed);
    };
    const pushManualTopic = (topic) => {
        const mapped = TEAM_MGMT_GUIDE_COMPAT_TOPIC_MAP[topic] ?? [];
        for (const entry of mapped) {
            push(entry);
        }
    };
    const topicValue = args?.topic;
    if (typeof topicValue === 'string') {
        if (topicValue === 'all') {
            for (const topic of spec_1.MANUAL_TOPICS) {
                pushManualTopic(topic);
            }
            return out;
        }
        if (spec_1.MANUAL_TOPICS.includes(topicValue)) {
            pushManualTopic(topicValue);
            return out;
        }
        push(topicValue);
        return out;
    }
    const topicsValue = args?.topics;
    if (Array.isArray(topicsValue) && topicsValue.length > 0) {
        for (const entry of topicsValue) {
            if (typeof entry !== 'string')
                continue;
            if (spec_1.MANUAL_TOPICS.includes(entry)) {
                pushManualTopic(entry);
                continue;
            }
            push(entry);
        }
        return out;
    }
    push('topics');
    return out;
}
async function renderTeamMgmtGuideViaMan(language, args) {
    const topics = normalizeTeamMgmtGuideTopics(args);
    return await (0, team_mgmt_manual_1.renderTeamMgmtGuideContent)(language, topics);
}
async function renderTeamMgmtGuideViaManualRequest(language, request) {
    const args = {};
    if (typeof request.topic === 'string') {
        args['topic'] = request.topic;
    }
    if (Array.isArray(request.topics)) {
        args['topics'] = [...request.topics];
    }
    const topics = normalizeTeamMgmtGuideTopics(args);
    return await (0, team_mgmt_manual_1.renderTeamMgmtGuideContent)(language, topics);
}
/**
 * Find the closest matching toolset name using substring matching and edit distance.
 * Priority: substring match > edit distance
 */
function findClosestToolset(input, toolsets) {
    if (toolsets.length === 0 || !input) {
        return null;
    }
    // First, try substring match (case-insensitive)
    const lowerInput = input.toLowerCase();
    for (const toolset of toolsets) {
        if (toolset.toLowerCase().includes(lowerInput) || lowerInput.includes(toolset.toLowerCase())) {
            return toolset;
        }
    }
    // Second, try edit distance (Levenshtein distance)
    let bestMatch = null;
    let bestDistance = Infinity;
    for (const toolset of toolsets) {
        const distance = levenshteinDistance(input.toLowerCase(), toolset.toLowerCase());
        if (distance < bestDistance) {
            bestDistance = distance;
            bestMatch = toolset;
        }
    }
    // Only suggest if the distance is reasonable (less than half the length of the shorter string)
    if (bestMatch && bestDistance <= Math.min(input.length, bestMatch.length) / 2) {
        return bestMatch;
    }
    return null;
}
/**
 * Calculate Levenshtein distance between two strings.
 */
function levenshteinDistance(a, b) {
    if (a.length === 0)
        return b.length;
    if (b.length === 0)
        return a.length;
    const matrix = [];
    // Initialize matrix
    for (let i = 0; i <= b.length; i++) {
        matrix[i] = [i];
    }
    for (let j = 0; j <= a.length; j++) {
        matrix[0][j] = j;
    }
    // Fill matrix
    for (let i = 1; i <= b.length; i++) {
        for (let j = 1; j <= a.length; j++) {
            if (b.charAt(i - 1) === a.charAt(j - 1)) {
                matrix[i][j] = matrix[i - 1][j - 1];
            }
            else {
                matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
                matrix[i][j - 1] + 1, // insertion
                matrix[i - 1][j] + 1);
            }
        }
    }
    return matrix[b.length][a.length];
}
