/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Hono } from 'hono';
import { getWorkspaceCwd } from '../middleware/workspace.js';
import { listAllServers, listServers, addServer, removeServer, installIntegration, } from '../../services/mcp-manager.js';
import { MCP_INTEGRATIONS } from '../../integrations/mcp-registry.js';
import { buildMcpEntry } from '../../integrations/mcp-setup.js';
const VALID_TARGETS = ['cursor', 'claude', 'vscode', 'windsurf'];
const VALID_SERVER_NAME = /^[\w@./-]+$/;
const ALLOWED_COMMANDS = new Set([
    'npx',
    'node',
    'python',
    'python3',
    'uvx',
    'docker',
    'deno',
    'bun',
    'pnpm',
    'yarn',
    'npm',
]);
function isValidTarget(value) {
    return VALID_TARGETS.includes(value);
}
function isValidServerName(name) {
    return VALID_SERVER_NAME.test(name) && name.length > 0 && name.length <= 128;
}
function isAllowedCommand(command) {
    const base = command.split('/').pop() ?? command;
    return ALLOWED_COMMANDS.has(base);
}
const SHELL_METACHARACTERS = /[;&|`$(){}[\]<>!#~*?\\\n\r]/;
function hasShellMetacharacters(value) {
    return SHELL_METACHARACTERS.test(value);
}
function validateArgs(args) {
    if (!Array.isArray(args))
        return null;
    for (const arg of args) {
        if (typeof arg !== 'string')
            return `Arg must be a string, got ${typeof arg}`;
        if (hasShellMetacharacters(arg))
            return `Arg contains forbidden shell metacharacter: ${arg}`;
        if (arg.length > 1024)
            return 'Arg exceeds 1024 characters';
    }
    return null;
}
function validateInstallTargets(targets) {
    if (!targets || targets.length === 0) {
        return 'Must specify at least one target';
    }
    for (const t of targets) {
        if (!isValidTarget(t)) {
            return `Invalid target: ${t}. Valid: ${VALID_TARGETS.join(', ')}`;
        }
    }
    return null;
}
function buildServerConfigFromEntry(entry) {
    const config = {};
    if (entry.url) {
        config.url = entry.url;
        config.type = entry.type;
    }
    else {
        if (entry.command)
            config.command = entry.command;
        if (entry.args)
            config.args = entry.args;
        if (entry.env)
            config.env = entry.env;
    }
    return config;
}
export function mcpRoute(cwd) {
    const app = new Hono();
    // GET /servers — list all configured MCP servers across all targets
    app.get('/servers', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const result = listAllServers(dir);
        return c.json({ targets: result });
    });
    // GET /servers/:target — list servers for a specific target
    app.get('/servers/:target', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const target = c.req.param('target');
        if (!isValidTarget(target)) {
            return c.json({ error: `Invalid target: ${target}. Valid: ${VALID_TARGETS.join(', ')}` }, 400);
        }
        const result = listServers(target, dir);
        return c.json(result);
    });
    // POST /servers/:target — add a new MCP server config
    app.post('/servers/:target', async (c) => {
        const target = c.req.param('target');
        if (!isValidTarget(target)) {
            return c.json({ error: `Invalid target: ${target}. Valid: ${VALID_TARGETS.join(', ')}` }, 400);
        }
        const body = await c.req.json();
        if (!body.name) {
            return c.json({ error: 'Missing required field: name' }, 400);
        }
        if (!isValidServerName(body.name)) {
            return c.json({ error: 'Invalid server name: must be alphanumeric, @, ., /, or -' }, 400);
        }
        const config = {};
        if (body.url) {
            config.url = body.url;
            config.type = 'http';
        }
        else {
            if (!body.command) {
                return c.json({ error: 'Either command or url is required' }, 400);
            }
            if (!isAllowedCommand(body.command)) {
                return c.json({ error: `Command "${body.command}" not in allowlist: ${[...ALLOWED_COMMANDS].join(', ')}` }, 400);
            }
            config.command = body.command;
            if (body.args) {
                const argsError = validateArgs(body.args);
                if (argsError)
                    return c.json({ error: `Invalid args: ${argsError}` }, 400);
                config.args = body.args;
            }
            if (body.env)
                config.env = body.env;
        }
        const dir = getWorkspaceCwd(c, cwd);
        const result = addServer(target, body.name, config, dir);
        return c.json({ added: true, name: body.name, target, path: result.path }, 201);
    });
    // DELETE /servers/:target/:name — remove a server from a target
    app.delete('/servers/:target/:name', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const target = c.req.param('target');
        const name = c.req.param('name');
        if (!isValidTarget(target)) {
            return c.json({ error: `Invalid target: ${target}. Valid: ${VALID_TARGETS.join(', ')}` }, 400);
        }
        const result = removeServer(target, name, dir);
        if (!result.removed) {
            return c.json({ error: `Server "${name}" not found in ${target}` }, 404);
        }
        return c.json({ removed: true, name, target, path: result.path });
    });
    // GET /integrations — return the known integrations catalog
    app.get('/integrations', (c) => {
        const catalog = MCP_INTEGRATIONS.map((i) => ({
            id: i.id,
            label: i.label,
            profiles: [...i.profiles],
            transport: i.config.transport,
            envVars: i.envVars.map((v) => ({ name: v.name, label: v.label, secret: v.secret })),
            setupGuide: i.setupGuide,
        }));
        return c.json({ integrations: catalog });
    });
    // POST /integrations/:id/install — install a known integration to specified targets
    app.post('/integrations/:id/install', async (c) => {
        const id = c.req.param('id');
        const integration = MCP_INTEGRATIONS.find((i) => i.id === id);
        if (!integration) {
            return c.json({ error: `Integration "${id}" not found` }, 404);
        }
        const body = await c.req.json();
        const targetError = validateInstallTargets(body.targets);
        if (targetError) {
            return c.json({ error: targetError }, 400);
        }
        let entry;
        try {
            entry = buildMcpEntry(integration, body.credentials ?? {});
        }
        catch (err) {
            return c.json({ error: `Failed to build integration config: ${err.message}` }, 400);
        }
        const dir = getWorkspaceCwd(c, cwd);
        const config = buildServerConfigFromEntry(entry);
        const validTargets = body.targets;
        const result = installIntegration(id, validTargets, config, dir);
        return c.json({ installed: true, id, targets: result.installed }, 201);
    });
    return app;
}
//# sourceMappingURL=mcp.js.map