import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { randomUUID } from 'node:crypto';
import { getProjectDir } from '../storage/mini-app-store.js';
import * as miniAppStore from '../storage/mini-app-store.js';
import { broadcastToWorkspace } from '../ws/connection-manager.js';
import { executePluginTool } from './plugin.js';
import { createBuiltinPluginApi } from './plugin-runtime-api.js';
import { createAgentRuntime } from '../adapters/agent-runtime.js';
import { createMiniAppFunctionTools } from './builtin-tools/mini-app-tools.js';
import { listPresets } from './agent.js';
import { listProviders } from '../storage/llm-store.js';
import { requestMiniAppClient } from './mini-app-client-rpc.js';
const miniAppToolRegistry = new Map();
/**
 * 编译 src/api.js：剥离 import 行（api.js 不依赖外部模块），把 ESM `export default`
 * 转 CJS `module.exports =`，在沙箱求值。默认导出应为 { methodName: handler }。
 * 复用 services 的编译约定（见 mini-app-services.ts）。
 */
export function compileApiJs(code) {
    let moduleObj;
    try {
        const stripped = code
            .replace(/^\s*import\s+.*$/gm, '')
            .replace(/\bexport\s+default\s+/, 'module.exports = ');
        moduleObj = { exports: {} };
        const fn = new Function('module', 'exports', stripped);
        fn(moduleObj, moduleObj.exports);
    }
    catch {
        return {};
    }
    const exported = moduleObj.exports;
    if (!exported || typeof exported !== 'object')
        return {};
    const handlers = {};
    for (const [name, h] of Object.entries(exported)) {
        if (typeof h === 'function')
            handlers[name] = h;
    }
    return handlers;
}
/**
 * 编译 src/tools.js：只读取工具元数据，不执行业务逻辑。
 * 支持 default export 数组，或 { tools: [...] }。
 */
export function compileToolsJs(code) {
    let moduleObj;
    try {
        const stripped = code
            .replace(/^\s*import\s+.*$/gm, '')
            .replace(/\bexport\s+default\s+/, 'module.exports = ');
        moduleObj = { exports: {} };
        const fn = new Function('module', 'exports', stripped);
        fn(moduleObj, moduleObj.exports);
    }
    catch {
        return [];
    }
    const exported = moduleObj.exports;
    const rawTools = Array.isArray(exported)
        ? exported
        : exported && typeof exported === 'object' && Array.isArray(exported.tools)
            ? exported.tools
            : [];
    return rawTools
        .map(normalizeToolSpec)
        .filter((tool) => !!tool);
}
function normalizeToolSpec(raw) {
    if (!raw || typeof raw !== 'object' || Array.isArray(raw))
        return null;
    const record = raw;
    const name = typeof record.name === 'string'
        ? record.name
        : typeof record.id === 'string'
            ? record.id
            : undefined;
    if (!name)
        return null;
    const description = typeof record.description === 'string' ? record.description : undefined;
    const inputSchema = record.inputSchema && typeof record.inputSchema === 'object' && !Array.isArray(record.inputSchema)
        ? record.inputSchema
        : { type: 'object', properties: {} };
    return { name, description, inputSchema };
}
/** 从项目目录加载 src/api.js 并编译。文件缺失返回空。 */
export function loadApiJs(projectId) {
    const filePath = join(getProjectDir(projectId), 'src', 'api.js');
    if (!existsSync(filePath))
        return {};
    try {
        return compileApiJs(readFileSync(filePath, 'utf-8'));
    }
    catch (err) {
        console.error(`[mini-app-agent] failed to load src/api.js:`, err instanceof Error ? err.message : err);
        return {};
    }
}
/** 从项目目录加载 src/tools.js 并注册到内存表。 */
export function loadMiniAppToolsJs(projectId) {
    const filePath = join(getProjectDir(projectId), 'src', 'tools.js');
    if (!existsSync(filePath))
        return {};
    try {
        const tools = compileToolsJs(readFileSync(filePath, 'utf-8'));
        return Object.fromEntries(tools.map((tool) => [tool.name, tool]));
    }
    catch (err) {
        console.error(`[mini-app-agent] failed to load src/tools.js:`, err instanceof Error ? err.message : err);
        return {};
    }
}
export function registerMiniAppTools(projectId) {
    const tools = loadMiniAppToolsJs(projectId);
    if (Object.keys(tools).length)
        miniAppToolRegistry.set(projectId, tools);
    else
        miniAppToolRegistry.delete(projectId);
    return tools;
}
export function registerAllMiniAppTools() {
    for (const project of miniAppStore.listProjects()) {
        registerMiniAppTools(project.id);
    }
}
export function getRegisteredMiniAppTools(projectId) {
    return miniAppToolRegistry.get(projectId) ?? registerMiniAppTools(projectId);
}
export function makeApiCtx(projectId) {
    return {
        projectId,
        broadcast: (event, data) => broadcastToWorkspace(projectId, event, data),
        callPluginTool: (pluginId, toolName, args) => executePluginTool(pluginId, toolName, args, createBuiltinPluginApi()),
        requestClient: (type, payload, timeoutMs) => requestMiniAppClient(projectId, type, payload, timeoutMs),
        readConfig: (path) => miniAppStore.readConfig(projectId, path),
        writeConfig: (path, value) => miniAppStore.writeConfig(projectId, path, value),
    };
}
/**
 * 把 api.js 方法表包装成 AgentFunctionTool[]。
 * 从 src/tools.js 注册表读取参数描述，注入到 inputSchema 和 description。
 */
export function buildApiFunctionTools(methods, ctxProvider, toolSpecs) {
    return Object.entries(methods).map(([name, handler]) => {
        const toolSpec = toolSpecs?.[name];
        return {
            name,
            description: toolSpec?.description ?? `${name} (project-defined api.js method)`,
            inputSchema: toolSpec?.inputSchema ?? { type: 'object', properties: {} },
            execute: async (input) => {
                const record = input && typeof input === 'object' && !Array.isArray(input)
                    ? input
                    : {};
                return handler(record, ctxProvider());
            },
        };
    });
}
export function createMiniAppToolsCatalogTool(currentProjectId) {
    return {
        name: 'get_mini_app_tools',
        description: 'Query the project-defined mini-app api.js tool metadata from src/tools.js by mini-app id.',
        inputSchema: {
            type: 'object',
            properties: {
                projectId: {
                    type: 'string',
                    description: 'Mini-app id. Defaults to the current mini-app when omitted.',
                },
            },
        },
        execute: async (input) => {
            const record = input && typeof input === 'object' && !Array.isArray(input)
                ? input
                : {};
            const projectId = typeof record.projectId === 'string' && record.projectId.trim()
                ? record.projectId.trim()
                : currentProjectId;
            const tools = Object.values(getRegisteredMiniAppTools(projectId));
            return { projectId, tools };
        },
    };
}
/**
 * 解析 agent 凭据优先级（spec §5.2）：
 * 1. agentId → 从 presets 提取 modelProvider/modelId/apiKey/apiBase 作为默认
 * 2. entry 本地字段覆盖 preset 值
 * 3. systemPrompt：entry 本地为准，缺失才用 preset
 * 4. 全都没有 → 返回空对象，调用方走服务端默认模型兜底
 */
export function resolveAgentCredentials(entry, presets) {
    const preset = entry.agentId ? presets.find((p) => p.id === entry.agentId) : undefined;
    const provider = resolveProvider(entry.providerId, entry.apiBase, entry.apiKey);
    return {
        modelProvider: entry.modelProvider ?? preset?.modelProvider,
        providerId: entry.providerId ?? preset?.providerId,
        modelId: entry.modelId ?? preset?.modelId,
        apiKey: provider?.apiKey ?? entry.apiKey ?? preset?.apiKey,
        apiBase: provider?.apiBase ?? entry.apiBase ?? preset?.apiBase,
        systemPrompt: entry.systemPrompt ?? preset?.systemPrompt,
        temperature: entry.temperature ?? preset?.temperature,
        maxTokens: entry.maxTokens ?? preset?.maxTokens,
    };
}
function resolveProvider(providerId, apiBase, apiKey) {
    const providers = listProviders();
    if (providerId) {
        const provider = providers.find((entry) => entry.id === providerId);
        if (provider)
            return provider;
    }
    if (!apiBase && !apiKey)
        return undefined;
    return providers.find((provider) => (!apiBase || provider.apiBase === apiBase)
        && (!apiKey || provider.apiKey === apiKey));
}
/**
 * 自包含执行路径：读 agents.json → 解析凭据 → 组装 functionTools（plugin + api.js）
 * → 注入路由/方法清单/systemPrompt → langchain execute → 落盘 user+agent 消息。
 * 不依赖 workspace。
 */
export async function runMiniAppAgent(input) {
    const { projectId, agentId, sessionId, message, route, onEvent, stopSignal } = input;
    const project = miniAppStore.getProject(projectId);
    if (!project)
        throw new Error(`Project not found: ${projectId}`);
    if (!project.enableAgents)
        throw new Error('Agents not enabled for this project');
    const configs = miniAppStore.readAgentsConfig(projectId);
    if (!configs)
        throw new Error('agents.json not found');
    const entry = configs.find((c) => c && c.id === agentId);
    if (!entry)
        throw new Error(`Agent not found in agents.json: ${agentId}`);
    const creds = resolveAgentCredentials(entry, listPresets(''));
    const runtimeConfig = {
        kind: 'langchain',
        ...(creds.modelProvider ? { provider: creds.modelProvider } : {}),
        ...(creds.modelId ? { model: creds.modelId } : {}),
        ...(creds.apiKey ? { apiKey: creds.apiKey } : {}),
        ...(creds.apiBase ? { baseURL: creds.apiBase } : {}),
    };
    const runtime = createAgentRuntime(runtimeConfig);
    const onAbort = () => runtime.stop();
    if (stopSignal)
        stopSignal.addEventListener('abort', onAbort, { once: true });
    try {
        // 组装 functionTools
        const functionTools = [];
        const toolsCfg = entry.tools ?? { api: true, plugin: true };
        if (toolsCfg.plugin) {
            functionTools.push(...createMiniAppFunctionTools({
                enabledPlugins: project.enabledPlugins ?? [],
            }));
        }
        let apiMethodNames = [];
        if (toolsCfg.api) {
            const apiMethods = loadApiJs(projectId);
            const apiToolSpecs = getRegisteredMiniAppTools(projectId);
            apiMethodNames = Object.keys(apiMethods);
            functionTools.push(createMiniAppToolsCatalogTool(projectId));
            if (apiMethodNames.length) {
                const ctxProvider = () => makeApiCtx(projectId);
                functionTools.push(...buildApiFunctionTools(apiMethods, ctxProvider, apiToolSpecs));
            }
        }
        // 拼 systemPrompt
        const sections = [];
        if (creds.systemPrompt)
            sections.push(creds.systemPrompt);
        sections.push(`Current mini-app route: ${route ?? '/'}`);
        sections.push(`Current mini-app id: ${projectId}`);
        if (apiMethodNames.length) {
            sections.push([
                `Available project api.js methods: ${apiMethodNames.join(', ')}.`,
                `Use get_mini_app_tools with the current mini-app id to inspect descriptions and input schemas before calling project api.js methods.`,
            ].join(' '));
        }
        if (toolsCfg.plugin && project.enabledPlugins?.length) {
            sections.push(`Enabled plugins: ${project.enabledPlugins.join(', ')}. ` +
                `Use list_plugin_tools / get_plugin_tool_detail / execute_plugin_tool.`);
        }
        const systemPrompt = sections.join('\n\n');
        // 执行
        // 用户消息时间戳在执行【前】捕获，确保严格早于 agent 回复时间戳，
        // 避免 user/agent 同毫秒落盘后历史排序错乱（重载后顺序反转）。
        const userTimestamp = new Date().toISOString();
        const output = [];
        const toolCalls = new Map();
        const result = await runtime.execute(message, getProjectDir(projectId), {
            systemPrompt,
            functionTools,
            maxTurns: 20,
            onEvent: (event) => {
                onEvent(event);
                if (event.type === 'output')
                    output.push(event.line);
                if (event.type === 'tool_use') {
                    toolCalls.set(event.id, { name: event.name, input: event.input, result: undefined });
                }
                if (event.type === 'tool_result' && event.toolUseId) {
                    const existing = toolCalls.get(event.toolUseId);
                    if (existing) {
                        toolCalls.set(event.toolUseId, { ...existing, result: event.result });
                    }
                }
            },
        });
        const userMessage = {
            id: randomUUID(), sessionId, agentId, role: 'user',
            content: message, route, timestamp: userTimestamp,
        };
        const agentContent = result.success
            ? (result.output.join('\n').trim() || result.summary)
            : `Error: ${result.error ?? result.summary}`;
        const agentMessage = {
            id: randomUUID(), sessionId, agentId, role: 'agent',
            content: agentContent, route, timestamp: new Date().toISOString(),
            ...(toolCalls.size ? { toolCalls: Array.from(toolCalls.values()) } : {}),
        };
        miniAppStore.saveAgentChat(projectId, userMessage);
        miniAppStore.saveAgentChat(projectId, agentMessage);
        return { userMessage, agentMessage };
    }
    finally {
        if (stopSignal)
            stopSignal.removeEventListener('abort', onAbort);
    }
}
//# sourceMappingURL=mini-app-agent.js.map