
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { mapFindFirst } from '../../../../base/common/arraysFind.js';
import { assertNever } from '../../../../base/common/assert.js';
import { disposableTimeout } from '../../../../base/common/async.js';
import { parse } from '../../../../base/common/jsonc.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import '../../../../base/common/observableInternal/index.js';
import { basename } from '../../../../base/common/resources.js';
import { URI } from '../../../../base/common/uri.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import { localize } from '../../../../nls.js';
import { ICommandService } from '../../../../platform/commands/common/commands.service.js';
import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IFileService } from '../../../../platform/files/common/files.service.js';
import { ILabelService } from '../../../../platform/label/common/label.service.js';
import { McpServerType } from '../../../../platform/mcp/common/mcpPlatformTypes.js';
import { RegistryType } from '../../../../platform/mcp/common/mcpManagement.js';
import { INotificationService } from '../../../../platform/notification/common/notification.service.js';
import { IOpenerService } from '../../../../platform/opener/common/opener.service.js';
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.service.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.service.js';
import { WorkbenchState, isWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.service.js';
import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.service.js';
import { IEditorService } from '../../../services/editor/common/editorService.service.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.service.js';
import { IWorkbenchMcpManagementService } from '../../../services/mcp/common/mcpWorkbenchManagementService.service.js';
import { McpCommandIds } from '../common/mcpCommandIds.js';
import { mcpDiscoverySection, allDiscoverySources, mcpStdioServerSchema } from '../common/mcpConfiguration.js';
import { IMcpRegistry } from '../common/mcpRegistryTypes.service.js';
import { McpConnectionState } from '../common/mcpTypes.js';
import { IMcpService } from '../common/mcpTypes.service.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { autorun } from '../../../../base/common/observableInternal/reactions/autorun.js';

var AddConfigurationType;
(function(AddConfigurationType) {
    AddConfigurationType[AddConfigurationType["Stdio"] = 0] = "Stdio";
    AddConfigurationType[AddConfigurationType["HTTP"] = 1] = "HTTP";
    AddConfigurationType[AddConfigurationType["NpmPackage"] = 2] = "NpmPackage";
    AddConfigurationType[AddConfigurationType["PipPackage"] = 3] = "PipPackage";
    AddConfigurationType[AddConfigurationType["NuGetPackage"] = 4] = "NuGetPackage";
    AddConfigurationType[AddConfigurationType["DockerImage"] = 5] = "DockerImage";
})(AddConfigurationType || (AddConfigurationType = {}));
const AssistedTypes = {
    [AddConfigurationType.NpmPackage]: {
        title: ( localize(10392, "Enter NPM Package Name")),
        placeholder: ( localize(10393, "Package name (e.g., @org/package)")),
        pickLabel: ( localize(10394, "NPM Package")),
        pickDescription: ( localize(10395, "Install from an NPM package name")),
        enabledConfigKey: null
    },
    [AddConfigurationType.PipPackage]: {
        title: ( localize(10396, "Enter Pip Package Name")),
        placeholder: ( localize(10397, "Package name (e.g., package-name)")),
        pickLabel: ( localize(10398, "Pip Package")),
        pickDescription: ( localize(10399, "Install from a Pip package name")),
        enabledConfigKey: null
    },
    [AddConfigurationType.NuGetPackage]: {
        title: ( localize(10400, "Enter NuGet Package Name")),
        placeholder: ( localize(10401, "Package name (e.g., Package.Name)")),
        pickLabel: ( localize(10402, "NuGet Package")),
        pickDescription: ( localize(10403, "Install from a NuGet package name")),
        enabledConfigKey: "chat.mcp.assisted.nuget.enabled"
    },
    [AddConfigurationType.DockerImage]: {
        title: ( localize(10404, "Enter Docker Image Name")),
        placeholder: ( localize(10405, "Image name (e.g., mcp/imagename)")),
        pickLabel: ( localize(10406, "Docker Image")),
        pickDescription: ( localize(10407, "Install from a Docker image")),
        enabledConfigKey: null
    }
};
var AddConfigurationCopilotCommand;
(function(AddConfigurationCopilotCommand) {
    AddConfigurationCopilotCommand["IsSupported"] = "github.copilot.chat.mcp.setup.check";
    AddConfigurationCopilotCommand["ValidatePackage"] = "github.copilot.chat.mcp.setup.validatePackage";
    AddConfigurationCopilotCommand["StartFlow"] = "github.copilot.chat.mcp.setup.flow";
})(AddConfigurationCopilotCommand || (AddConfigurationCopilotCommand = {}));
let McpAddConfigurationCommand = class McpAddConfigurationCommand {
    constructor(
        workspaceFolder,
        _quickInputService,
        _mcpManagementService,
        _workspaceService,
        _environmentService,
        _commandService,
        _mcpRegistry,
        _openerService,
        _editorService,
        _fileService,
        _notificationService,
        _telemetryService,
        _mcpService,
        _label,
        _configurationService
    ) {
        this.workspaceFolder = workspaceFolder;
        this._quickInputService = _quickInputService;
        this._mcpManagementService = _mcpManagementService;
        this._workspaceService = _workspaceService;
        this._environmentService = _environmentService;
        this._commandService = _commandService;
        this._mcpRegistry = _mcpRegistry;
        this._openerService = _openerService;
        this._editorService = _editorService;
        this._fileService = _fileService;
        this._notificationService = _notificationService;
        this._telemetryService = _telemetryService;
        this._mcpService = _mcpService;
        this._label = _label;
        this._configurationService = _configurationService;
    }
    async getServerType() {
        const items = [{
            kind: AddConfigurationType.Stdio,
            label: ( localize(10408, "Command (stdio)")),
            description: ( localize(10409, "Run a local command that implements the MCP protocol"))
        }, {
            kind: AddConfigurationType.HTTP,
            label: ( localize(10410, "HTTP (HTTP or Server-Sent Events)")),
            description: ( localize(10411, "Connect to a remote HTTP server that implements the MCP protocol"))
        }];
        let aiSupported;
        try {
            aiSupported = await this._commandService.executeCommand(AddConfigurationCopilotCommand.IsSupported);
        } catch {}
        if (aiSupported) {
            items.unshift({
                type: "separator",
                label: ( localize(10412, "Manual Install"))
            });
            const elligableTypes = ( Object.entries(AssistedTypes).map((
                [type, {
                    pickLabel,
                    pickDescription,
                    enabledConfigKey
                }]
            ) => {
                if (enabledConfigKey) {
                    const enabled = this._configurationService.getValue(enabledConfigKey) ?? false;
                    if (!enabled) {
                        return;
                    }
                }
                return {
                    kind: Number(type),
                    label: pickLabel,
                    description: pickDescription
                };
            })).filter(x => !!x);
            items.push({
                type: "separator",
                label: ( localize(10413, "Model-Assisted"))
            }, ...elligableTypes);
        }
        items.push({
            type: "separator"
        });
        const discovery = this._configurationService.getValue(mcpDiscoverySection);
        if (discovery && typeof discovery === "object" && ( allDiscoverySources.some(d => !discovery[d]))) {
            items.push({
                kind: "discovery",
                label: ( localize(10414, "Add from another application..."))
            });
        }
        items.push({
            kind: "browse",
            label: ( localize(10415, "Browse MCP Servers..."))
        });
        const result = await this._quickInputService.pick(items, {
            placeHolder: ( localize(10416, "Choose the type of MCP server to add"))
        });
        if (result?.kind === "browse") {
            this._commandService.executeCommand(McpCommandIds.Browse);
            return undefined;
        }
        if (result?.kind === "discovery") {
            this._commandService.executeCommand("workbench.action.openSettings", mcpDiscoverySection);
            return undefined;
        }
        return result?.kind;
    }
    async getStdioConfig() {
        const command = await this._quickInputService.input({
            title: ( localize(10417, "Enter Command")),
            placeHolder: ( localize(10418, "Command to run (with optional arguments)")),
            ignoreFocusLost: true
        });
        if (!command) {
            return undefined;
        }
        this._telemetryService.publicLog2("mcp.addserver", {
            packageType: "stdio"
        });
        const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g);
        return {
            type: McpServerType.LOCAL,
            command: parts[0].replace(/"/g, ""),
            args: ( parts.slice(1).map(arg => arg.replace(/"/g, "")))
        };
    }
    async getSSEConfig() {
        const url = await this._quickInputService.input({
            title: ( localize(10419, "Enter Server URL")),
            placeHolder: ( localize(10420, "URL of the MCP server (e.g., http://localhost:3000)")),
            ignoreFocusLost: true
        });
        if (!url) {
            return undefined;
        }
        this._telemetryService.publicLog2("mcp.addserver", {
            packageType: "sse"
        });
        return {
            url,
            type: McpServerType.REMOTE
        };
    }
    async getServerId(suggestion = `my-mcp-server-${generateUuid().split("-")[0]}`) {
        const id = await this._quickInputService.input({
            title: ( localize(10421, "Enter Server ID")),
            placeHolder: ( localize(10422, "Unique identifier for this server")),
            value: suggestion,
            ignoreFocusLost: true
        });
        return id;
    }
    async getConfigurationTarget() {
        const options = [{
            target: ConfigurationTarget.USER_LOCAL,
            label: ( localize(10423, "Global")),
            description: ( localize(10424, "Available in all workspaces, runs locally"))
        }];
        const raLabel = this._environmentService.remoteAuthority && this._label.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority);
        if (raLabel) {
            options.push({
                target: ConfigurationTarget.USER_REMOTE,
                label: ( localize(10425, "Remote")),
                description: ( localize(10426, "Available on this remote machine, runs on {0}", raLabel))
            });
        }
        const workbenchState = this._workspaceService.getWorkbenchState();
        if (workbenchState !== WorkbenchState.EMPTY) {
            const target = workbenchState === WorkbenchState.FOLDER ? this._workspaceService.getWorkspace().folders[0] : ConfigurationTarget.WORKSPACE;
            if (this._environmentService.remoteAuthority) {
                options.push({
                    target,
                    label: ( localize(10427, "Workspace")),
                    description: ( localize(10428, "Available in this workspace, runs on {0}", raLabel))
                });
            } else {
                options.push({
                    target,
                    label: ( localize(10427, "Workspace")),
                    description: ( localize(10429, "Available in this workspace, runs locally"))
                });
            }
        }
        if (options.length === 1) {
            return options[0].target;
        }
        const targetPick = await this._quickInputService.pick(options, {
            title: ( localize(10430, "Add MCP Server")),
            placeHolder: ( localize(10431, "Select the configuration target"))
        });
        return targetPick?.target;
    }
    async getAssistedConfig(type) {
        const packageName = await this._quickInputService.input({
            ignoreFocusLost: true,
            title: AssistedTypes[type].title,
            placeHolder: AssistedTypes[type].placeholder
        });
        if (!packageName) {
            return undefined;
        }
        let LoadAction;
        (function(LoadAction) {
            LoadAction["Retry"] = "retry";
            LoadAction["Cancel"] = "cancel";
            LoadAction["Allow"] = "allow";
            LoadAction["OpenUri"] = "openUri";
        })(LoadAction || (LoadAction = {}));
        const loadingQuickPickStore = ( new DisposableStore());
        const loadingQuickPick = loadingQuickPickStore.add(this._quickInputService.createQuickPick());
        loadingQuickPick.title = ( localize(10432, "Loading package details..."));
        loadingQuickPick.busy = true;
        loadingQuickPick.ignoreFocusOut = true;
        const packageType = this.getPackageType(type);
        this._telemetryService.publicLog2("mcp.addserver", {
            packageType: packageType
        });
        this._commandService.executeCommand(AddConfigurationCopilotCommand.ValidatePackage, {
            type: packageType,
            name: packageName,
            targetConfig: {
                ...mcpStdioServerSchema,
                properties: {
                    ...mcpStdioServerSchema.properties,
                    name: {
                        type: "string",
                        description: "Suggested name of the server, alphanumeric and hyphen only"
                    }
                },
                required: [...(mcpStdioServerSchema.required || []), "name"]
            }
        }).then(result => {
            if (!result || result.state === "error") {
                loadingQuickPick.title = result?.error || "Unknown error loading package";
                const items = [];
                if (result?.helpUri) {
                    items.push({
                        id: LoadAction.OpenUri,
                        label: result.helpUriLabel ?? ( localize(10433, "Open help URL")),
                        helpUri: ( URI.parse(result.helpUri))
                    });
                }
                items.push({
                    id: LoadAction.Retry,
                    label: ( localize(10434, "Try a different package"))
                }, {
                    id: LoadAction.Cancel,
                    label: ( localize(10435, "Cancel"))
                });
                loadingQuickPick.items = items;
            } else {
                loadingQuickPick.title = ( localize(
                    10436,
                    "Install {0}{1} from {2}?",
                    result.name ?? packageName,
                    result.version ? `@${result.version}` : "",
                    result.publisher
                ));
                loadingQuickPick.items = [{
                    id: LoadAction.Allow,
                    label: ( localize(10437, "Allow"))
                }, {
                    id: LoadAction.Cancel,
                    label: ( localize(10435, "Cancel"))
                }];
            }
            loadingQuickPick.busy = false;
        });
        const loadingAction = await ( new Promise(resolve => {
            loadingQuickPickStore.add(
                loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0]))
            );
            loadingQuickPickStore.add(loadingQuickPick.onDidHide(() => resolve(undefined)));
            loadingQuickPick.show();
        })).finally(() => loadingQuickPickStore.dispose());
        switch (loadingAction?.id) {
        case LoadAction.Retry:
            return this.getAssistedConfig(type);
        case LoadAction.OpenUri:
            if (loadingAction.helpUri) {
                this._openerService.open(loadingAction.helpUri);
            }
            return undefined;
        case LoadAction.Allow:
            break;
        case LoadAction.Cancel:
        default:
            return undefined;
        }
        const config = await this._commandService.executeCommand(AddConfigurationCopilotCommand.StartFlow, {
            name: packageName,
            type: packageType
        });
        if (config?.type === "mapped") {
            return {
                name: config.name,
                server: config.server,
                inputs: config.inputs
            };
        } else if (config?.type === "assisted" || !config?.type) {
            return config;
        } else {
            assertNever();
        }
    }
    showOnceDiscovered(name) {
        const store = ( new DisposableStore());
        store.add(autorun(reader => {
            const colls = this._mcpRegistry.collections.read(reader);
            const servers = this._mcpService.servers.read(reader);
            const match = mapFindFirst(colls, collection => mapFindFirst(
                collection.serverDefinitions.read(reader),
                server => server.label === name ? {
                    server,
                    collection
                } : undefined
            ));
            const server = match && servers.find(s => s.definition.id === match.server.id);
            if (match && server) {
                if (match.collection.presentation?.origin) {
                    this._editorService.openEditor({
                        resource: match.collection.presentation.origin,
                        options: {
                            selection: match.server.presentation?.origin?.range,
                            preserveFocus: true
                        }
                    });
                } else {
                    this._commandService.executeCommand(McpCommandIds.ServerOptions, name);
                }
                server.start({
                    promptType: "all-untrusted"
                }).then(state => {
                    if (state.state === McpConnectionState.Kind.Error) {
                        server.showOutput();
                    }
                });
                store.dispose();
            }
        }));
        store.add(disposableTimeout(() => store.dispose(), 5000));
    }
    async run() {
        const serverType = await this.getServerType();
        if (serverType === undefined) {
            return;
        }
        let config;
        let suggestedName;
        let inputs;
        let inputValues;
        switch (serverType) {
        case AddConfigurationType.Stdio:
            config = await this.getStdioConfig();
            break;
        case AddConfigurationType.HTTP:
            config = await this.getSSEConfig();
            break;
        case AddConfigurationType.NpmPackage:
        case AddConfigurationType.PipPackage:
        case AddConfigurationType.NuGetPackage:
        case AddConfigurationType.DockerImage:
            {
                const r = await this.getAssistedConfig(serverType);
                config = r?.server ? {
                    ...r.server,
                    type: McpServerType.LOCAL
                } : undefined;
                suggestedName = r?.name;
                inputs = r?.inputs;
                inputValues = r?.inputValues;
                break;
            }
        default:
            assertNever();
        }
        if (!config) {
            return;
        }
        const name = await this.getServerId(suggestedName);
        if (!name) {
            return;
        }
        let target = this.workspaceFolder;
        if (!target) {
            target = await this.getConfigurationTarget();
            if (!target) {
                return;
            }
        }
        await this._mcpManagementService.install({
            name,
            config,
            inputs
        }, {
            target
        });
        if (inputValues) {
            for (const [key, value] of Object.entries(inputValues)) {
                await this._mcpRegistry.setSavedInput(
                    key,
                    (isWorkspaceFolder(target) ? ConfigurationTarget.WORKSPACE_FOLDER : target) ?? ConfigurationTarget.WORKSPACE,
                    value
                );
            }
        }
        const packageType = this.getPackageType(serverType);
        if (packageType) {
            this._telemetryService.publicLog2("mcp.addserver.completed", {
                packageType,
                serverType: config.type,
                target: target === ConfigurationTarget.WORKSPACE ? "workspace" : "user"
            });
        }
        this.showOnceDiscovered(name);
    }
    async pickForUrlHandler(resource, showIsPrimary = false) {
        const name = decodeURIComponent(basename(resource)).replace(/\.json$/, "");
        const placeHolder = ( localize(10438, "Install MCP server {0}", name));
        const items = [{
            id: "install",
            label: ( localize(10439, "Install Server"))
        }, {
            id: "show",
            label: ( localize(10440, "Show Configuration", name))
        }, {
            id: "rename",
            label: ( localize(10441, "Rename \"{0}\"", name))
        }, {
            id: "cancel",
            label: ( localize(10435, "Cancel"))
        }];
        if (showIsPrimary) {
            [items[0], items[1]] = [items[1], items[0]];
        }
        const pick = await this._quickInputService.pick(items, {
            placeHolder,
            ignoreFocusLost: true
        });
        const getEditors = () => this._editorService.findEditors(resource);
        switch (pick?.id) {
        case "show":
            await this._editorService.openEditor({
                resource
            });
            break;
        case "install":
            await this._editorService.save(getEditors());
            try {
                const contents = await this._fileService.readFile(resource);
                const {
                    inputs,
                    ...config
                } = parse(( contents.value.toString()));
                await this._mcpManagementService.install({
                    name,
                    config,
                    inputs
                });
                this._editorService.closeEditors(getEditors());
                this.showOnceDiscovered(name);
            } catch (e) {
                this._notificationService.error(( localize(10442, "Error installing MCP server {0}: {1}", name, e.message)));
                await this._editorService.openEditor({
                    resource
                });
            }
            break;
        case "rename":
            {
                const newName = await this._quickInputService.input({
                    placeHolder: ( localize(10443, "Enter new name")),
                    value: name
                });
                if (newName) {
                    const newURI = resource.with({
                        path: `/${encodeURIComponent(newName)}.json`
                    });
                    await this._editorService.save(getEditors());
                    await this._fileService.move(resource, newURI);
                    return this.pickForUrlHandler(newURI, showIsPrimary);
                }
                break;
            }
        }
    }
    getPackageType(serverType) {
        switch (serverType) {
        case AddConfigurationType.NpmPackage:
            return "npm";
        case AddConfigurationType.PipPackage:
            return "pip";
        case AddConfigurationType.NuGetPackage:
            return "nuget";
        case AddConfigurationType.DockerImage:
            return "docker";
        case AddConfigurationType.Stdio:
            return "stdio";
        case AddConfigurationType.HTTP:
            return "sse";
        default:
            return undefined;
        }
    }
};
McpAddConfigurationCommand = ( __decorate([( __param(1, IQuickInputService)), ( __param(2, IWorkbenchMcpManagementService)), ( __param(3, IWorkspaceContextService)), ( __param(4, IWorkbenchEnvironmentService)), ( __param(5, ICommandService)), ( __param(6, IMcpRegistry)), ( __param(7, IOpenerService)), ( __param(8, IEditorService)), ( __param(9, IFileService)), ( __param(10, INotificationService)), ( __param(11, ITelemetryService)), ( __param(12, IMcpService)), ( __param(13, ILabelService)), ( __param(14, IConfigurationService))], McpAddConfigurationCommand));
let McpInstallFromManifestCommand = class McpInstallFromManifestCommand {
    constructor(
        _fileDialogService,
        _fileService,
        _quickInputService,
        _notificationService,
        _mcpManagementService,
        _logService
    ) {
        this._fileDialogService = _fileDialogService;
        this._fileService = _fileService;
        this._quickInputService = _quickInputService;
        this._notificationService = _notificationService;
        this._mcpManagementService = _mcpManagementService;
        this._logService = _logService;
    }
    async run() {
        const result = await this._fileDialogService.showOpenDialog({
            title: ( localize(10444, "Select MCP Server Manifest")),
            filters: [{
                name: ( localize(10445, "MCP Manifest")),
                extensions: ["json"]
            }],
            canSelectFiles: true,
            canSelectMany: false,
            openLabel: ( localize(10446, "&&Install"))
        });
        if (!result?.[0]) {
            return;
        }
        const manifestUri = result[0];
        let manifest;
        try {
            const contents = await this._fileService.readFile(manifestUri);
            manifest = parse(( contents.value.toString()));
        } catch (e) {
            this._notificationService.error(( localize(10447, "Failed to read manifest file: {0}", e.message)));
            return;
        }
        if (!manifest || typeof manifest !== "object") {
            this._notificationService.error(( localize(10448, "Invalid manifest file: expected a JSON object")));
            return;
        }
        const galleryManifest = manifest;
        let packageType;
        if (Array.isArray(galleryManifest.packages) && galleryManifest.packages.length > 0) {
            packageType = galleryManifest.packages[0].registryType;
        } else if (Array.isArray(galleryManifest.remotes) && galleryManifest.remotes.length > 0) {
            packageType = RegistryType.REMOTE;
        } else {
            this._notificationService.error(( localize(
                10449,
                "Invalid manifest: expected 'packages' or 'remotes' with at least one entry"
            )));
            return;
        }
        let config;
        let inputs;
        try {
            const {
                mcpServerConfiguration,
                notices
            } = this._mcpManagementService.getMcpServerConfigurationFromManifest(galleryManifest, packageType);
            config = mcpServerConfiguration.config;
            inputs = mcpServerConfiguration.inputs;
            if (notices.length > 0) {
                this._logService.warn(
                    `MCP Management Service: Warnings while installing the MCP server from ${manifestUri.path}`,
                    notices
                );
            }
        } catch (e) {
            this._notificationService.error(( localize(10450, "Failed to parse manifest: {0}", e.message)));
            return;
        }
        let name = galleryManifest.name;
        if (!name) {
            name = await this._quickInputService.input({
                title: ( localize(10451, "Enter Server ID")),
                placeHolder: ( localize(10452, "Unique identifier for this server")),
                value: basename(manifestUri).replace(/\.json$/i, ""),
                ignoreFocusLost: true
            });
            if (!name) {
                return;
            }
        }
        try {
            await this._mcpManagementService.install({
                name,
                config,
                inputs
            });
            this._notificationService.info(( localize(10453, "MCP server '{0}' installed successfully", name)));
        } catch (e) {
            this._notificationService.error(( localize(10454, "Failed to install MCP server: {0}", e.message)));
        }
    }
};
McpInstallFromManifestCommand = ( __decorate([( __param(0, IFileDialogService)), ( __param(1, IFileService)), ( __param(2, IQuickInputService)), ( __param(3, INotificationService)), ( __param(4, IWorkbenchMcpManagementService)), ( __param(5, ILogService))], McpInstallFromManifestCommand));

export { AddConfigurationType, AssistedTypes, McpAddConfigurationCommand, McpInstallFromManifestCommand };
