import { McpClient } from 'react-mcp-hook/client';
import { getAttachmentToolDefinitions } from '../attachmentTools';
import { DEFAULT_TOOL_PERMISSION } from './constants';
import { buildToolIdentifier, normalizeToolPermissions } from './helpers';
const ATTACHMENT_SERVER_ID = 'attachments';
const ATTACHMENT_SERVER_NAME = 'Attachments';
const buildAttachmentReferenceMap = (attachments) => {
    if (!attachments || attachments.length === 0) {
        return undefined;
    }
    const referenceMap = {};
    for (const entry of attachments) {
        if (!entry || typeof entry !== 'object') {
            continue;
        }
        const id = typeof entry.id === 'string' ? entry.id.trim() : '';
        if (!id) {
            continue;
        }
        const name = typeof entry.name === 'string' ? entry.name : `Attachment ${id}`;
        const mimeType = typeof entry.mimeType === 'string' ? entry.mimeType : 'application/octet-stream';
        const sourceUrl = typeof entry.contentUrl === 'string' ? entry.contentUrl : undefined;
        referenceMap[id] = {
            id,
            name,
            mimeType,
            size: 0,
            url: sourceUrl,
            fileId: undefined,
            sourceUrl
        };
    }
    return Object.keys(referenceMap).length > 0 ? referenceMap : undefined;
};
const prepareAttachmentTools = (agent, attachments) => {
    const definitions = [];
    const tools = [];
    const attachmentDefinitions = getAttachmentToolDefinitions();
    if (attachmentDefinitions.length === 0) {
        return { definitions, tools };
    }
    const attachmentContext = buildAttachmentReferenceMap(attachments);
    const runtime = {
        attachments: attachmentContext,
        getAttachmentCache: agent.options.getAttachmentCache
    };
    for (const definition of attachmentDefinitions) {
        if (!definition || typeof definition !== 'object') {
            continue;
        }
        const description = typeof definition.description === 'string'
            ? definition.description
            : undefined;
        const parameters = definition.parameters && typeof definition.parameters === 'object'
            ? definition.parameters
            : { type: 'object' };
        definitions.push({
            name: definition.name,
            description,
            parameters
        });
        tools.push({
            kind: 'attachment',
            uniqueName: definition.name,
            originalName: definition.name,
            permission: 'enabled',
            serverId: ATTACHMENT_SERVER_ID,
            serverName: ATTACHMENT_SERVER_NAME,
            description,
            parameters,
            async execute(input) {
                const sanitizedInput = input && typeof input === 'object'
                    ? input
                    : undefined;
                const result = await definition.handler(sanitizedInput, runtime);
                return result;
            }
        });
    }
    return { definitions, tools };
};
export const createOllamaToolContext = async (agent, options = {}) => {
    const servers = Array.isArray(agent.options.mcpServers) ? agent.options.mcpServers : [];
    const toolsByName = new Map();
    const toolDefinitions = [];
    const clients = [];
    const attachmentTools = prepareAttachmentTools(agent, options.attachments);
    for (const definition of attachmentTools.definitions) {
        toolDefinitions.push(definition);
    }
    for (const tool of attachmentTools.tools) {
        toolsByName.set(tool.uniqueName, tool);
    }
    const logger = (event, details) => {
        if (agent.options.logLevel === 'debug') {
            console.debug(`[Ollama MCP] ${event}`, details);
        }
    };
    for (const server of servers) {
        const serverEnabled = server.options.serverEnabled ?? true;
        if (!serverEnabled) {
            continue;
        }
        const url = typeof server.options.url === 'string' ? server.options.url.trim() : '';
        if (!url) {
            logger('server-url-missing', { serverId: server.id });
            continue;
        }
        const client = new McpClient({
            url,
            headers: server.options.headers,
            fetchTimeoutMs: server.options.fetchTimeoutMs
        });
        clients.push({ client, serverId: server.id });
        let tools;
        try {
            const fetchedTools = await client.listTools();
            tools = Array.isArray(fetchedTools)
                ? fetchedTools
                : [];
        }
        catch (error) {
            console.warn(`Failed to list tools for MCP server "${server.id}"`, error);
            continue;
        }
        const permissions = new Map(normalizeToolPermissions(server.options.toolPreferences));
        for (const rawTool of tools) {
            if (!rawTool || typeof rawTool !== 'object') {
                continue;
            }
            const name = typeof rawTool.name === 'string' && rawTool.name.length > 0
                ? rawTool.name
                : undefined;
            if (!name) {
                continue;
            }
            const permission = permissions.get(name) ?? DEFAULT_TOOL_PERMISSION;
            if (permission === 'disabled') {
                continue;
            }
            const uniqueName = buildToolIdentifier(server.id, name);
            const description = typeof rawTool.description === 'string' ? rawTool.description : undefined;
            const inputSchema = rawTool.inputSchema?.schema ?? rawTool.inputSchema;
            const prepared = {
                kind: 'mcp',
                uniqueName,
                originalName: name,
                serverId: server.id,
                serverName: server.options.serverName,
                permission,
                client,
                description,
                parameters: inputSchema
            };
            toolsByName.set(uniqueName, prepared);
            toolDefinitions.push({
                name: uniqueName,
                description: prepared.description,
                parameters: prepared.parameters && typeof prepared.parameters === 'object'
                    ? prepared.parameters
                    : { type: 'object' }
            });
        }
    }
    return {
        definitions: toolDefinitions,
        toolsByName,
        dispose: async () => {
            await Promise.allSettled(clients.map(async ({ client, serverId }) => {
                try {
                    client.abortSse();
                    client.reset('Ollama MCP tool context disposed');
                    client.rejectAllPendingRequests(new Error('MCP client disposed'));
                }
                catch (error) {
                    logger('client-dispose-error', { serverId, error });
                }
            }));
        }
    };
};
