import { z } from "zod";
import { WorkspaceConfigManager, serviceSchema } from "@skyramp/skyramp";
import { createRequire } from "module";
import fs from "fs/promises";
import yaml from "js-yaml";
import { logger } from "../../utils/logger.js";
import { AnalyticsService } from "../../services/AnalyticsService.js";
import { SKYRAMP_IMAGE_VERSION } from "../../utils/versions.js";
import { validateAndConsumeScanToken } from "./initScanWorkspaceTool.js";
import { ALL_AUTH_TYPES } from "../../utils/workspaceAuth.js";
const require = createRequire(import.meta.url);
const MCP_VERSION = require("../../../package.json").version || "";
function getExecutorVersion() {
    return SKYRAMP_IMAGE_VERSION;
}
const TOOL_NAME = "skyramp_init_workspace";
// Full authType set including cookie / session / token which @skyramp/skyramp's
// serviceSchema does not yet accept. writeWorkspaceConfig() writes YAML directly
// to bypass the library's Zod enum restriction for these types.
// Once @skyramp/skyramp publishes an updated serviceSchema, the direct-write
// path can be replaced with manager.addService().
// ALL_AUTH_TYPES is now imported from workspaceAuth.ts (single source of truth).
// Extend the library's serviceSchema to accept cookie/session/token authTypes,
// which @skyramp/skyramp's serviceSchema does not yet support.
const localServiceSchema = serviceSchema.extend({
    api: z
        .object({
        schemaPath: z.string().optional(),
        authType: z.enum(ALL_AUTH_TYPES).optional(),
        authHeader: z.string().optional(),
        authScheme: z.string().optional(),
        baseUrl: z.string().optional(),
    })
        .optional(),
});
const initializeWorkspaceSchema = z.object({
    workspacePath: z
        .string()
        .describe("Path to workspace directory"),
    services: z
        .array(localServiceSchema)
        .describe("Array of services discovered by skyramp_init_scan. Must contain at least one service."),
    scanToken: z
        .string()
        .describe("Token returned by skyramp_init_scan. Required to confirm the repo was scanned before initializing."),
    force: z
        .boolean()
        .default(false)
        .describe("Set to true to overwrite an existing workspace file. Use when the user explicitly requests it, or when recovering from a workspace validation failure (schema mismatch, unknown fields). Default is false."),
});
/**
 * Write a YAML workspace config to disk, bypassing the library's restricted
 * authType enum. Uses the same YAML format as @skyramp/skyramp's writeWorkspaceFile.
 */
async function writeWorkspaceConfig(manager, config) {
    const header = "# Skyramp Workspace Configuration\n";
    const body = yaml.dump(config, {
        lineWidth: 120,
        noRefs: true,
        quotingType: '"',
        forceQuotes: false,
    });
    await fs.writeFile(manager.getConfigPath(), header + body, "utf8");
}
export function registerInitializeWorkspaceTool(server) {
    server.registerTool(TOOL_NAME, {
        description: "PREREQUISITE: Call skyramp_init_scan first to get a scanToken and discover services. " +
            "Then call this tool to write .skyramp/workspace.yml with the discovered services and scanToken.",
        inputSchema: initializeWorkspaceSchema.shape,
        _meta: {
            keywords: [
                "initialize",
                "setup",
                "configure",
                "workspace",
                "config",
                "init",
            ],
        },
    }, async (params) => {
        let errorResult;
        const recordParams = {
            workspacePath: params.workspacePath,
            hasParams: String(Object.keys(params).length),
            force: String(params.force || false),
        };
        try {
            const workspacePath = params.workspacePath;
            const manager = new WorkspaceConfigManager(workspacePath);
            // Check if already initialized
            if ((await manager.exists()) && !params.force) {
                return {
                    content: [
                        {
                            type: "text",
                            text: `Workspace already initialized at ${manager.getConfigPath()}. Set force: true to overwrite.`,
                        },
                    ],
                    isError: false,
                };
            }
            // Validate scan token
            if (!validateAndConsumeScanToken(params.scanToken)) {
                return {
                    content: [
                        {
                            type: "text",
                            text: "Invalid or expired scanToken. Call skyramp_init_scan first to get a valid scanToken and scanning instructions.",
                        },
                    ],
                    isError: true,
                };
            }
            if (!params.services || params.services.length === 0) {
                return {
                    content: [
                        {
                            type: "text",
                            text: "No services provided. Follow the scanning instructions from skyramp_init_scan to discover services, then call this tool again.",
                        },
                    ],
                    isError: true,
                };
            }
            // Initialize workspace
            let config = await manager.initialize();
            config = await manager.updateMetadata({
                mcpVersion: MCP_VERSION,
                executorVersion: getExecutorVersion(),
            });
            // Batch all service upserts: update in memory, write once.
            // Always uses the direct-write path so extended auth types (cookie /
            // session / token) that @skyramp/skyramp's serviceSchema does not yet
            // accept are handled without N+1 reads or a mid-loop validation error.
            const services = (config.services ?? []);
            for (const svc of params.services) {
                const idx = services.findIndex((s) => s.serviceName === svc.serviceName);
                if (idx >= 0) {
                    services[idx] = svc;
                }
                else {
                    services.push(svc);
                }
            }
            config.services = services;
            if (config.metadata) {
                config.metadata.updatedAt = new Date().toISOString();
            }
            await writeWorkspaceConfig(manager, config);
            // Build result summary
            const lines = [];
            if (config.workspace?.repoName) {
                lines.push(`Repository: ${config.workspace.repoName}`);
            }
            if (config.workspace?.repoUrl) {
                lines.push(`URL: ${config.workspace.repoUrl}`);
            }
            if (config.services && config.services.length > 0) {
                lines.push(`\nServices (${config.services.length}):`);
                for (const svc of config.services) {
                    const parts = [svc.serviceName];
                    if (svc.language)
                        parts.push(svc.language);
                    if (svc.framework)
                        parts.push(svc.framework);
                    if (svc.testDirectory)
                        parts.push(`@ ${svc.testDirectory}`);
                    lines.push(`  - ${parts.join(" | ")}`);
                    if (svc.api?.schemaPath)
                        lines.push(`    API: ${svc.api.schemaPath}`);
                    if (svc.runtimeDetails?.runtime)
                        lines.push(`    Runtime: ${svc.runtimeDetails.runtime}`);
                }
            }
            const resultMessage = `Workspace initialized: ${manager.getConfigPath()}\n\n${lines.join("\n")}`;
            return {
                content: [{ type: "text", text: resultMessage }],
                isError: false,
            };
        }
        catch (error) {
            const errorMessage = `Failed to initialize workspace: ${error.message || error}`;
            logger.error(errorMessage, error);
            errorResult = {
                content: [{ type: "text", text: errorMessage }],
                isError: true,
            };
            return errorResult;
        }
        finally {
            AnalyticsService.pushMCPToolEvent(TOOL_NAME, errorResult, recordParams);
        }
    });
}
