import { jsx as _jsx } from "react/jsx-runtime";
import React, { useEffect } from 'react';
import { useMcp } from 'react-mcp-hook';
import { useMcpServers } from '../contexts/McpContext';
import ServerSetupView from './ServerSetupView';
import ServerCheckingView from './ServerCheckingView';
import ServerErrorView from './ServerErrorView';
import ToolRunnerView from './ToolRunnerView';
import ServerDashboardView from './ServerDashboardView';
const recordsEqual = (a, b) => {
    const aEntries = Object.entries(a ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
    const bEntries = Object.entries(b ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
    if (aEntries.length !== bEntries.length) {
        return false;
    }
    for (let index = 0; index < aEntries.length; index++) {
        const [aKey, aValue] = aEntries[index];
        const [bKey, bValue] = bEntries[index];
        if (aKey !== bKey || aValue !== bValue) {
            return false;
        }
    }
    return true;
};
const arraysEqual = (a, b) => {
    const aList = Array.isArray(a) ? a : [];
    const bList = Array.isArray(b) ? b : [];
    if (aList.length !== bList.length) {
        return false;
    }
    return aList.every((value, index) => value === bList[index]);
};
const coerceStringRecord = (input) => {
    if (!input || typeof input !== 'object' || Array.isArray(input)) {
        return {};
    }
    const result = {};
    for (const [rawKey, rawValue] of Object.entries(input)) {
        const key = String(rawKey).trim();
        if (!key)
            continue;
        if (rawValue === undefined || rawValue === null) {
            continue;
        }
        const value = typeof rawValue === 'string' ? rawValue.trim() : String(rawValue).trim();
        if (!value)
            continue;
        result[key] = value;
    }
    return result;
};
const coerceStringArray = (input) => {
    if (!Array.isArray(input)) {
        return [];
    }
    const result = [];
    for (const entry of input) {
        if (typeof entry !== 'string') {
            continue;
        }
        const value = entry.trim();
        if (value.length === 0) {
            continue;
        }
        result.push(value);
    }
    return result;
};
const getConnectionCacheKey = (options) => {
    if (!options) {
        return null;
    }
    if (options.transportMode === 'stdio') {
        const command = options.stdio?.command ?? '';
        const args = Array.isArray(options.stdio?.args) ? options.stdio?.args.join(' ') : '';
        const summary = [command, args].filter((part) => part && part.length > 0).join(' ').trim();
        return summary.length > 0 ? `stdio:${summary}` : 'stdio';
    }
    return options.url ?? null;
};
const areConnectionOptionsEqual = (a, b) => {
    if (a === b)
        return true;
    if (!a || !b)
        return false;
    if (a.transportMode !== b.transportMode)
        return false;
    if ((a.serverName ?? '') !== (b.serverName ?? ''))
        return false;
    if (a.transportMode === 'stdio') {
        const aConfig = a.stdio;
        const bConfig = b.stdio;
        if (!aConfig || !bConfig) {
            return false;
        }
        if (aConfig.command !== bConfig.command) {
            return false;
        }
        if ((aConfig.autoRestart ?? true) !== (bConfig.autoRestart ?? true)) {
            return false;
        }
        if (!arraysEqual(aConfig.args, bConfig.args)) {
            return false;
        }
        return recordsEqual(aConfig.env ?? undefined, bConfig.env ?? undefined);
    }
    if ((a.url ?? '') !== (b.url ?? ''))
        return false;
    return recordsEqual(a.headers, b.headers);
};
export const McpServer = ({ uniqueId, onDelete, showStats = true, showTools = true, showPrompts = true }) => {
    const { getServer, updateServer, setServerEnabled, setToolPermission, getToolPermission: getServerToolPermission, setServerPrompts } = useMcpServers();
    const [oldConnectionOptions, setOldConnectionOptions] = React.useState(null);
    const [connectionOptions, setConnectionOptions] = React.useState(null);
    const [isHealthy, setIsHealthy] = React.useState(null);
    const [focusedTool, setFocusedTool] = React.useState(null);
    const promptFetchKeyRef = React.useRef(null);
    const hasFetchedPromptsRef = React.useRef(false);
    const [promptsLoading, setPromptsLoading] = React.useState(false);
    const [promptsError, setPromptsError] = React.useState(null);
    const serverRecord = getServer(uniqueId);
    const serverEnabled = serverRecord?.serverEnabled ?? true;
    const prompts = serverRecord?.cachedPrompts ?? [];
    // Dynamically load STDIO transport plugin only in Node.js/Electron environments
    const [stdioTransport, setStdioTransport] = React.useState(null);
    const [stdioTransportLoading, setStdioTransportLoading] = React.useState(false);
    // Create a stable cache key for the STDIO config
    const stdioConfigKey = React.useMemo(() => {
        if (!connectionOptions || connectionOptions.transportMode !== 'stdio') {
            return null;
        }
        const config = connectionOptions.stdio;
        if (!config?.command) {
            return null;
        }
        return JSON.stringify({
            command: config.command,
            args: config.args,
            env: config.env,
            autoRestart: config.autoRestart ?? true
        });
    }, [connectionOptions]);
    React.useEffect(() => {
        // Check if we're in a Node.js/Electron environment (not pure browser)
        const hasNodeCapabilities = typeof process !== 'undefined' && process.versions?.node;
        console.log('IS NODE: ', hasNodeCapabilities);
        console.log('connectionOptions:', connectionOptions);
        console.log('stdioConfigKey:', stdioConfigKey);
        if (!hasNodeCapabilities) {
            // Pure browser environment - STDIO not supported
            console.log('No Node capabilities, returning');
            setStdioTransportLoading(false);
            return;
        }
        if (!stdioConfigKey) {
            console.log('No valid STDIO config');
            setStdioTransport(null);
            setStdioTransportLoading(false);
            return;
        }
        console.log('Loading STDIO transport plugin for config:', stdioConfigKey);
        setStdioTransportLoading(true);
        // Dynamic import to avoid bundling Node.js modules in browser build
        import('../lib/mcp/stdioTransportPlugin')
            .then((module) => {
            console.log('STDIO transport plugin loaded successfully');
            const parsed = JSON.parse(stdioConfigKey);
            const config = module.createStdioTransportConfig({
                command: parsed.command,
                args: parsed.args,
                env: parsed.env,
                autoRestart: parsed.autoRestart,
                timeoutMs: 30000
            });
            console.log('STDIO transport config created:', config);
            setStdioTransport(config);
            setStdioTransportLoading(false);
        })
            .catch((error) => {
            console.error('Failed to load STDIO transport plugin:', error);
            setStdioTransport(null);
            setStdioTransportLoading(false);
        });
    }, [stdioConfigKey]);
    const useMcpOptions = React.useMemo(() => {
        console.log('useMcpOptions memo recalculating - connectionOptions:', connectionOptions, 'stdioTransport:', stdioTransport);
        if (!connectionOptions) {
            return null;
        }
        if (connectionOptions.transportMode === 'stdio') {
            if (!stdioTransport) {
                console.log('STDIO mode but no stdioTransport yet');
                return null;
            }
            console.log('Creating STDIO useMcpOptions with transport:', stdioTransport);
            return {
                transport: stdioTransport
            };
        }
        if (connectionOptions.transportMode !== 'sse') {
            return null;
        }
        const url = connectionOptions.url?.trim();
        if (!url) {
            return null;
        }
        const headers = connectionOptions.headers && Object.keys(connectionOptions.headers).length > 0
            ? connectionOptions.headers
            : undefined;
        return {
            url,
            headers
        };
    }, [connectionOptions, stdioTransport]);
    const mcp = useMcp(useMcpOptions);
    const { fetchTools, callTool, refetch, tools, error, abort, loading, serverInfo, protocolVersion, capabilities, lastEvent, listPrompts } = mcp;
    console.log('MCP hook state:', {
        tools: tools.length,
        error,
        loading,
        serverInfo,
        protocolVersion,
        capabilities
    });
    const autoFetchEnabled = serverRecord?.autoFetch ?? false;
    const supportsPromptExecution = typeof mcp.getPrompt === 'function';
    const fetchToolsRef = React.useRef(fetchTools);
    const abortRef = React.useRef(abort);
    useEffect(() => {
        fetchToolsRef.current = fetchTools;
    }, [fetchTools]);
    useEffect(() => {
        abortRef.current = abort;
    }, [abort]);
    const viewState = React.useMemo(() => {
        if (!connectionOptions) {
            return { kind: 'setup', oldOptions: oldConnectionOptions };
        }
        // If STDIO mode and transport is still loading, show checking state
        if (connectionOptions.transportMode === 'stdio' && stdioTransportLoading) {
            return { kind: 'checking', options: connectionOptions };
        }
        // If we don't have valid MCP options yet (incomplete config), stay in setup
        if (!useMcpOptions) {
            return { kind: 'setup', oldOptions: connectionOptions };
        }
        if (isHealthy === null) {
            return { kind: 'checking', options: connectionOptions };
        }
        if (isHealthy === false) {
            return { kind: 'error', options: connectionOptions, error };
        }
        if (focusedTool) {
            return { kind: 'tool', options: connectionOptions, tool: focusedTool };
        }
        return {
            kind: 'dashboard',
            options: connectionOptions,
            serverEnabled,
            tools,
            headers: connectionOptions.headers ?? {},
            autoFetchEnabled,
            loading,
            serverInfo,
            protocolVersion,
            capabilities,
            lastEvent,
            prompts,
            promptsLoading,
            promptsError,
            supportsPromptExecution
        };
    }, [
        autoFetchEnabled,
        connectionOptions,
        error,
        focusedTool,
        isHealthy,
        oldConnectionOptions,
        protocolVersion,
        serverEnabled,
        stdioTransportLoading,
        tools,
        serverInfo,
        capabilities,
        lastEvent,
        loading,
        prompts,
        promptsLoading,
        promptsError,
        supportsPromptExecution,
        useMcpOptions
    ]);
    const handleDelete = React.useCallback(() => {
        abort();
        onDelete?.(uniqueId);
    }, [abort, onDelete, uniqueId]);
    const handleServerToggle = React.useCallback((enabled) => {
        setServerEnabled(uniqueId, enabled);
    }, [setServerEnabled, uniqueId]);
    const handleConnectionSubmit = React.useCallback((opts) => {
        const transportMode = opts.transportMode ?? 'sse';
        const normalizedOpts = transportMode === 'stdio'
            ? (() => {
                const source = opts.stdio ?? { command: '' };
                const args = Array.isArray(source.args) && source.args.length > 0 ? source.args : undefined;
                const env = source.env && Object.keys(source.env).length > 0 ? source.env : undefined;
                return {
                    serverName: opts.serverName,
                    transportMode: 'stdio',
                    stdio: {
                        command: source.command,
                        ...(args ? { args } : {}),
                        ...(env ? { env } : {}),
                        autoRestart: source.autoRestart ?? true
                    }
                };
            })()
            : {
                serverName: opts.serverName,
                transportMode: 'sse',
                url: opts.url ?? '',
                headers: opts.headers ?? {}
            };
        setConnectionOptions(normalizedOpts);
        setOldConnectionOptions(normalizedOpts);
        const existing = getServer(uniqueId);
        updateServer(uniqueId, {
            ...(existing ?? {}),
            transportMode: normalizedOpts.transportMode,
            url: normalizedOpts.transportMode === 'sse' ? normalizedOpts.url : undefined,
            autoFetch: existing?.autoFetch ?? true,
            headers: normalizedOpts.transportMode === 'sse' ? normalizedOpts.headers : undefined,
            stdioConfig: normalizedOpts.transportMode === 'stdio' ? normalizedOpts.stdio : undefined,
            serverName: normalizedOpts.serverName,
            toolPreferences: existing?.toolPreferences ?? {},
            serverEnabled: existing?.serverEnabled ?? true,
            cachedPrompts: existing?.cachedPrompts,
            cachedPromptsUpdatedAt: existing?.cachedPromptsUpdatedAt
        });
    }, [getServer, uniqueId, updateServer]);
    const handleConnectionCancel = React.useCallback((previous) => {
        setConnectionOptions(previous);
    }, []);
    const handleEditConnection = React.useCallback(() => {
        setConnectionOptions(null);
    }, []);
    const handleToolPermissionChange = React.useCallback((toolName, permission) => {
        setToolPermission(uniqueId, toolName, permission);
    }, [setToolPermission, uniqueId]);
    const handleFocusTool = React.useCallback((tool) => {
        setFocusedTool(tool);
    }, []);
    const fetchAllPrompts = React.useCallback(async () => {
        const load = async (cursor, collected = []) => {
            const result = await listPrompts(cursor ? { cursor } : undefined);
            if (result.error) {
                throw result.error;
            }
            const combined = [...collected, ...result.prompts];
            if (result.nextCursor) {
                return await load(result.nextCursor, combined);
            }
            return combined;
        };
        return await load(undefined, []);
    }, [listPrompts]);
    useEffect(() => {
        const saved = getServer(uniqueId);
        if (!saved) {
            return;
        }
        const inferredTransport = saved.transportMode === 'stdio' || saved.stdioConfig ? 'stdio' : 'sse';
        let normalized;
        if (inferredTransport === 'stdio') {
            const stdioConfig = saved.stdioConfig ?? { command: '', autoRestart: true };
            const command = typeof stdioConfig.command === 'string' ? stdioConfig.command.trim() : '';
            const args = coerceStringArray(stdioConfig.args);
            const envRecord = coerceStringRecord(stdioConfig.env);
            normalized = {
                serverName: saved.serverName ?? (command || 'New MCP Server'),
                transportMode: 'stdio',
                stdio: {
                    command,
                    ...(args.length > 0 ? { args } : {}),
                    ...(Object.keys(envRecord).length > 0 ? { env: envRecord } : {}),
                    autoRestart: typeof stdioConfig.autoRestart === 'boolean' ? stdioConfig.autoRestart : true
                }
            };
        }
        else {
            const headersRecord = coerceStringRecord(saved.headers);
            normalized = {
                serverName: saved.serverName ?? saved.url ?? 'New MCP Server',
                transportMode: 'sse',
                url: saved.url ?? '',
                headers: headersRecord
            };
        }
        setOldConnectionOptions((prev) => (areConnectionOptionsEqual(prev, normalized) ? prev : normalized));
        setConnectionOptions((prev) => (areConnectionOptionsEqual(prev, normalized) ? prev : normalized));
    }, [getServer, uniqueId]);
    useEffect(() => {
        const key = getConnectionCacheKey(connectionOptions);
        if (promptFetchKeyRef.current !== key) {
            promptFetchKeyRef.current = key;
            hasFetchedPromptsRef.current = false;
        }
    }, [connectionOptions]);
    useEffect(() => {
        if (!connectionOptions) {
            console.log('Health check: no connectionOptions');
            setIsHealthy(null);
            return;
        }
        // Don't run health check if we don't have valid MCP options yet
        if (!useMcpOptions) {
            console.log('Health check: no useMcpOptions yet');
            setIsHealthy(null);
            return;
        }
        console.log('Health check: calling fetchTools');
        setIsHealthy(null);
        let cancelled = false;
        fetchToolsRef.current()
            .then(() => {
            console.log('Health check: fetchTools succeeded');
            if (!cancelled)
                setIsHealthy(true);
        })
            .catch((err) => {
            console.log('Health check: fetchTools failed', err);
            if (!cancelled)
                setIsHealthy(false);
        });
        return () => {
            cancelled = true;
            abortRef.current?.();
        };
    }, [connectionOptions, useMcpOptions]);
    useEffect(() => {
        if (!connectionOptions || isHealthy !== true || !serverEnabled) {
            return;
        }
        const hasCachedPrompts = (serverRecord?.cachedPrompts?.length ?? 0) > 0;
        if (hasFetchedPromptsRef.current) {
            return;
        }
        hasFetchedPromptsRef.current = true;
        let cancelled = false;
        setPromptsLoading(!hasCachedPrompts);
        setPromptsError(null);
        fetchAllPrompts()
            .then((loadedPrompts) => {
            if (cancelled)
                return;
            setServerPrompts(uniqueId, loadedPrompts.length > 0 ? loadedPrompts : undefined);
        })
            .catch((err) => {
            if (cancelled)
                return;
            setPromptsError(err instanceof Error ? err.message : 'Failed to load prompts.');
        })
            .finally(() => {
            if (cancelled)
                return;
            setPromptsLoading(false);
        });
        return () => {
            cancelled = true;
        };
    }, [connectionOptions, fetchAllPrompts, isHealthy, serverEnabled, serverRecord?.cachedPrompts?.length, serverRecord?.cachedPromptsUpdatedAt, setServerPrompts, uniqueId]);
    const handlePromptsRefresh = React.useCallback(async () => {
        if (!connectionOptions || isHealthy !== true || !serverEnabled) {
            return;
        }
        hasFetchedPromptsRef.current = true;
        setPromptsLoading(true);
        setPromptsError(null);
        try {
            const loadedPrompts = await fetchAllPrompts();
            setServerPrompts(uniqueId, loadedPrompts.length > 0 ? loadedPrompts : undefined);
        }
        catch (err) {
            setPromptsError(err instanceof Error ? err.message : 'Failed to load prompts.');
        }
        finally {
            setPromptsLoading(false);
        }
    }, [connectionOptions, fetchAllPrompts, isHealthy, serverEnabled, setServerPrompts, uniqueId]);
    useEffect(() => {
        if (!serverEnabled) {
            hasFetchedPromptsRef.current = false;
            setPromptsLoading(false);
            setPromptsError(null);
            setServerPrompts(uniqueId, undefined);
            return;
        }
        hasFetchedPromptsRef.current = false;
    }, [serverEnabled, setServerPrompts, uniqueId]);
    const handleRefreshAll = React.useCallback(() => {
        void refetch();
        void handlePromptsRefresh();
    }, [handlePromptsRefresh, refetch]);
    const handleRunPrompt = React.useCallback(async (promptName, args) => {
        const getPrompt = mcp.getPrompt;
        if (typeof getPrompt !== 'function') {
            return {
                prompt: undefined,
                error: new Error('Prompt execution is not supported by this MCP client.')
            };
        }
        try {
            const result = await getPrompt(promptName, args);
            if (result.error) {
                return {
                    prompt: result.prompt,
                    error: result.error
                };
            }
            return {
                prompt: result.prompt
            };
        }
        catch (error) {
            return {
                prompt: undefined,
                error: error instanceof Error ? error : new Error('Failed to fetch prompt output.')
            };
        }
    }, [mcp]);
    useEffect(() => {
        if (!serverRecord || tools.length === 0) {
            return;
        }
        const existingPreferences = serverRecord.toolPreferences ?? {};
        const normalizedPreferences = {};
        for (const tool of tools) {
            const currentValue = existingPreferences[tool.name] ?? 'enabled';
            normalizedPreferences[tool.name] = currentValue;
        }
        let changed = false;
        const existingKeys = Object.keys(existingPreferences);
        const normalizedKeys = Object.keys(normalizedPreferences);
        if (existingKeys.length !== normalizedKeys.length) {
            changed = true;
        }
        else {
            for (const key of normalizedKeys) {
                if (existingPreferences[key] !== normalizedPreferences[key]) {
                    changed = true;
                    break;
                }
            }
        }
        if (changed) {
            updateServer(uniqueId, {
                ...serverRecord,
                toolPreferences: normalizedPreferences
            });
        }
    }, [serverRecord, tools, updateServer, uniqueId]);
    switch (viewState.kind) {
        case 'setup':
            return (_jsx(ServerSetupView, { oldOptions: viewState.oldOptions, onSubmit: handleConnectionSubmit, onCancel: handleConnectionCancel, onDelete: handleDelete }));
        case 'checking':
            return _jsx(ServerCheckingView, { options: viewState.options, onDelete: handleDelete });
        case 'error':
            return (_jsx(ServerErrorView, { options: viewState.options, error: viewState.error, onRetry: refetch, onEdit: handleEditConnection, onDelete: handleDelete }));
        case 'tool':
            return (_jsx(ToolRunnerView, { tool: viewState.tool, serverName: viewState.options.serverName, onBack: () => handleFocusTool(null), callTool: callTool }));
        case 'dashboard':
            return (_jsx(ServerDashboardView, { options: viewState.options, tools: viewState.tools, serverEnabled: viewState.serverEnabled, onToggleServer: handleServerToggle, onEdit: handleEditConnection, onRefresh: handleRefreshAll, onDelete: handleDelete, onViewTool: handleFocusTool, getToolPermission: (toolName) => getServerToolPermission(uniqueId, toolName), onChangeToolPermission: handleToolPermissionChange, headers: viewState.headers, autoFetchEnabled: viewState.autoFetchEnabled, showStats: showStats, showTools: showTools, showPrompts: showPrompts, serverInfo: viewState.serverInfo, protocolVersion: viewState.protocolVersion, capabilities: viewState.capabilities, lastEvent: viewState.lastEvent, isLoadingTools: viewState.loading, prompts: viewState.prompts, promptsError: viewState.promptsError, isLoadingPrompts: viewState.promptsLoading, onRefreshPrompts: handlePromptsRefresh, onRunPrompt: viewState.supportsPromptExecution ? handleRunPrompt : undefined, supportsPromptExecution: viewState.supportsPromptExecution }));
        default:
            return null;
    }
};
export default McpServer;
