import { confirm, input, select } from "@inquirer/prompts";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { basename, join, resolve } from "path";
import { CURRENT_MANIFEST_VERSION } from "../schemas.js";
export function readPackageJson(dirPath) {
    const packageJsonPath = join(dirPath, "package.json");
    if (existsSync(packageJsonPath)) {
        try {
            return JSON.parse(readFileSync(packageJsonPath, "utf-8"));
        }
        catch (e) {
            // Ignore package.json parsing errors
        }
    }
    return {};
}
export function getDefaultAuthorName(packageData) {
    if (typeof packageData.author === "string") {
        return packageData.author;
    }
    return packageData.author?.name || "";
}
export function getDefaultAuthorEmail(packageData) {
    if (typeof packageData.author === "object") {
        return packageData.author?.email || "";
    }
    return "";
}
export function getDefaultAuthorUrl(packageData) {
    if (typeof packageData.author === "object") {
        return packageData.author?.url || "";
    }
    return "";
}
export function getDefaultRepositoryUrl(packageData) {
    if (typeof packageData.repository === "string") {
        return packageData.repository;
    }
    return packageData.repository?.url || "";
}
export function getDefaultBasicInfo(packageData, resolvedPath) {
    const name = packageData.name || basename(resolvedPath);
    const authorName = getDefaultAuthorName(packageData) || "Unknown Author";
    const displayName = name;
    const version = packageData.version || "1.0.0";
    const description = packageData.description || "A MCPB bundle";
    return { name, authorName, displayName, version, description };
}
export function getDefaultAuthorInfo(packageData) {
    return {
        authorEmail: getDefaultAuthorEmail(packageData),
        authorUrl: getDefaultAuthorUrl(packageData),
    };
}
export function getDefaultServerConfig(packageData) {
    const serverType = "node";
    const entryPoint = getDefaultEntryPoint(serverType, packageData);
    const mcp_config = createMcpConfig(serverType, entryPoint);
    return { serverType, entryPoint, mcp_config };
}
export function getDefaultOptionalFields(packageData) {
    return {
        keywords: "",
        license: packageData.license || "MIT",
        repository: undefined,
    };
}
export function createMcpConfig(serverType, entryPoint) {
    switch (serverType) {
        case "node":
            return {
                command: "node",
                args: ["${__dirname}/" + entryPoint],
                env: {},
            };
        case "python":
            return {
                command: "python",
                args: ["${__dirname}/" + entryPoint],
                env: {
                    PYTHONPATH: "${__dirname}/server/lib",
                },
            };
        case "binary":
            return {
                command: "${__dirname}/" + entryPoint,
                args: [],
                env: {},
            };
    }
}
export function getDefaultEntryPoint(serverType, packageData) {
    switch (serverType) {
        case "node":
            return packageData?.main || "server/index.js";
        case "python":
            return "server/main.py";
        case "binary":
            return "server/my-server";
    }
}
export async function promptBasicInfo(packageData, resolvedPath) {
    const defaultName = packageData.name || basename(resolvedPath);
    const name = await input({
        message: "Extension name:",
        default: defaultName,
        validate: (value) => value.trim().length > 0 || "Name is required",
    });
    const authorName = await input({
        message: "Author name:",
        default: getDefaultAuthorName(packageData),
        validate: (value) => value.trim().length > 0 || "Author name is required",
    });
    const displayName = await input({
        message: "Display name (optional):",
        default: name,
    });
    const version = await input({
        message: "Version:",
        default: packageData.version || "1.0.0",
        validate: (value) => {
            if (!value.trim())
                return "Version is required";
            if (!/^\d+\.\d+\.\d+/.test(value)) {
                return "Version must follow semantic versioning (e.g., 1.0.0)";
            }
            return true;
        },
    });
    const description = await input({
        message: "Description:",
        default: packageData.description || "",
        validate: (value) => value.trim().length > 0 || "Description is required",
    });
    return { name, authorName, displayName, version, description };
}
export async function promptAuthorInfo(packageData) {
    const authorEmail = await input({
        message: "Author email (optional):",
        default: getDefaultAuthorEmail(packageData),
    });
    const authorUrl = await input({
        message: "Author URL (optional):",
        default: getDefaultAuthorUrl(packageData),
    });
    return { authorEmail, authorUrl };
}
export async function promptServerConfig(packageData) {
    const serverType = (await select({
        message: "Server type:",
        choices: [
            { name: "Node.js", value: "node" },
            { name: "Python", value: "python" },
            { name: "Binary", value: "binary" },
        ],
        default: "node",
    }));
    const entryPoint = await input({
        message: "Entry point:",
        default: getDefaultEntryPoint(serverType, packageData),
    });
    const mcp_config = createMcpConfig(serverType, entryPoint);
    return { serverType, entryPoint, mcp_config };
}
export async function promptTools() {
    const addTools = await confirm({
        message: "Does your MCP Server provide tools you want to advertise (optional)?",
        default: true,
    });
    const tools = [];
    let toolsGenerated = false;
    if (addTools) {
        let addMore = true;
        while (addMore) {
            const toolName = await input({
                message: "Tool name:",
                validate: (value) => value.trim().length > 0 || "Tool name is required",
            });
            const toolDescription = await input({
                message: "Tool description (optional):",
            });
            tools.push({
                name: toolName,
                ...(toolDescription ? { description: toolDescription } : {}),
            });
            addMore = await confirm({
                message: "Add another tool?",
                default: false,
            });
        }
        // Ask about generated tools
        toolsGenerated = await confirm({
            message: "Does your server generate additional tools at runtime?",
            default: false,
        });
    }
    return { tools, toolsGenerated };
}
export async function promptPrompts() {
    const addPrompts = await confirm({
        message: "Does your MCP Server provide prompts you want to advertise (optional)?",
        default: false,
    });
    const prompts = [];
    let promptsGenerated = false;
    if (addPrompts) {
        let addMore = true;
        while (addMore) {
            const promptName = await input({
                message: "Prompt name:",
                validate: (value) => value.trim().length > 0 || "Prompt name is required",
            });
            const promptDescription = await input({
                message: "Prompt description (optional):",
            });
            // Ask about arguments
            const hasArguments = await confirm({
                message: "Does this prompt have arguments?",
                default: false,
            });
            const argumentNames = [];
            if (hasArguments) {
                let addMoreArgs = true;
                while (addMoreArgs) {
                    const argName = await input({
                        message: "Argument name:",
                        validate: (value) => {
                            if (!value.trim())
                                return "Argument name is required";
                            if (argumentNames.includes(value)) {
                                return "Argument names must be unique";
                            }
                            return true;
                        },
                    });
                    argumentNames.push(argName);
                    addMoreArgs = await confirm({
                        message: "Add another argument?",
                        default: false,
                    });
                }
            }
            // Prompt for the text template
            const promptText = await input({
                message: hasArguments
                    ? `Prompt text (use \${arguments.name} for arguments: ${argumentNames.join(", ")}):`
                    : "Prompt text:",
                validate: (value) => value.trim().length > 0 || "Prompt text is required",
            });
            prompts.push({
                name: promptName,
                ...(promptDescription ? { description: promptDescription } : {}),
                ...(argumentNames.length > 0 ? { arguments: argumentNames } : {}),
                text: promptText,
            });
            addMore = await confirm({
                message: "Add another prompt?",
                default: false,
            });
        }
        // Ask about generated prompts
        promptsGenerated = await confirm({
            message: "Does your server generate additional prompts at runtime?",
            default: false,
        });
    }
    return { prompts, promptsGenerated };
}
export async function promptOptionalFields(packageData) {
    const keywords = await input({
        message: "Keywords (comma-separated, optional):",
        default: "",
    });
    const license = await input({
        message: "License:",
        default: packageData.license || "MIT",
    });
    const addRepository = await confirm({
        message: "Add repository information?",
        default: !!packageData.repository,
    });
    let repository;
    if (addRepository) {
        const repoUrl = await input({
            message: "Repository URL:",
            default: getDefaultRepositoryUrl(packageData),
        });
        if (repoUrl) {
            repository = {
                type: "git",
                url: repoUrl,
            };
        }
    }
    return { keywords, license, repository };
}
export async function promptLongDescription(description) {
    const hasLongDescription = await confirm({
        message: "Add a detailed long description?",
        default: false,
    });
    if (hasLongDescription) {
        const longDescription = await input({
            message: "Long description (supports basic markdown):",
            default: description,
        });
        return longDescription;
    }
    return undefined;
}
export async function promptUrls() {
    const homepage = await input({
        message: "Homepage URL (optional):",
        validate: (value) => {
            if (!value.trim())
                return true;
            try {
                new URL(value);
                return true;
            }
            catch {
                return "Must be a valid URL (e.g., https://example.com)";
            }
        },
    });
    const documentation = await input({
        message: "Documentation URL (optional):",
        validate: (value) => {
            if (!value.trim())
                return true;
            try {
                new URL(value);
                return true;
            }
            catch {
                return "Must be a valid URL";
            }
        },
    });
    const support = await input({
        message: "Support URL (optional):",
        validate: (value) => {
            if (!value.trim())
                return true;
            try {
                new URL(value);
                return true;
            }
            catch {
                return "Must be a valid URL";
            }
        },
    });
    return { homepage, documentation, support };
}
export async function promptVisualAssets() {
    const icon = await input({
        message: "Icon file path (optional, relative to manifest):",
        validate: (value) => {
            if (!value.trim())
                return true;
            if (value.includes(".."))
                return "Relative paths cannot include '..'";
            return true;
        },
    });
    const addScreenshots = await confirm({
        message: "Add screenshots?",
        default: false,
    });
    const screenshots = [];
    if (addScreenshots) {
        let addMore = true;
        while (addMore) {
            const screenshot = await input({
                message: "Screenshot file path (relative to manifest):",
                validate: (value) => {
                    if (!value.trim())
                        return "Screenshot path is required";
                    if (value.includes(".."))
                        return "Relative paths cannot include '..'";
                    return true;
                },
            });
            screenshots.push(screenshot);
            addMore = await confirm({
                message: "Add another screenshot?",
                default: false,
            });
        }
    }
    return { icon, screenshots };
}
export async function promptCompatibility(serverType) {
    const addCompatibility = await confirm({
        message: "Add compatibility constraints?",
        default: false,
    });
    if (!addCompatibility) {
        return undefined;
    }
    const addPlatforms = await confirm({
        message: "Specify supported platforms?",
        default: false,
    });
    let platforms;
    if (addPlatforms) {
        const selectedPlatforms = [];
        const supportsDarwin = await confirm({
            message: "Support macOS (darwin)?",
            default: true,
        });
        if (supportsDarwin)
            selectedPlatforms.push("darwin");
        const supportsWin32 = await confirm({
            message: "Support Windows (win32)?",
            default: true,
        });
        if (supportsWin32)
            selectedPlatforms.push("win32");
        const supportsLinux = await confirm({
            message: "Support Linux?",
            default: true,
        });
        if (supportsLinux)
            selectedPlatforms.push("linux");
        platforms = selectedPlatforms.length > 0 ? selectedPlatforms : undefined;
    }
    let runtimes;
    if (serverType !== "binary") {
        const addRuntimes = await confirm({
            message: "Specify runtime version constraints?",
            default: false,
        });
        if (addRuntimes) {
            if (serverType === "python") {
                const pythonVersion = await input({
                    message: "Python version constraint (e.g., >=3.8,<4.0):",
                    validate: (value) => value.trim().length > 0 || "Python version constraint is required",
                });
                runtimes = { python: pythonVersion };
            }
            else if (serverType === "node") {
                const nodeVersion = await input({
                    message: "Node.js version constraint (e.g., >=16.0.0):",
                    validate: (value) => value.trim().length > 0 || "Node.js version constraint is required",
                });
                runtimes = { node: nodeVersion };
            }
        }
    }
    return {
        ...(platforms ? { platforms } : {}),
        ...(runtimes ? { runtimes } : {}),
    };
}
export async function promptUserConfig() {
    const addUserConfig = await confirm({
        message: "Add user-configurable options?",
        default: false,
    });
    if (!addUserConfig) {
        return {};
    }
    const userConfig = {};
    let addMore = true;
    while (addMore) {
        const optionKey = await input({
            message: "Configuration option key (unique identifier):",
            validate: (value) => {
                if (!value.trim())
                    return "Key is required";
                if (userConfig[value])
                    return "Key must be unique";
                return true;
            },
        });
        const optionType = (await select({
            message: "Option type:",
            choices: [
                { name: "String", value: "string" },
                { name: "Number", value: "number" },
                { name: "Boolean", value: "boolean" },
                { name: "Directory", value: "directory" },
                { name: "File", value: "file" },
            ],
        }));
        const optionTitle = await input({
            message: "Option title (human-readable name):",
            validate: (value) => value.trim().length > 0 || "Title is required",
        });
        const optionDescription = await input({
            message: "Option description:",
            validate: (value) => value.trim().length > 0 || "Description is required",
        });
        const optionRequired = await confirm({
            message: "Is this option required?",
            default: false,
        });
        const optionSensitive = await confirm({
            message: "Is this option sensitive (like a password)?",
            default: false,
        });
        // Build the option object
        const option = {
            type: optionType,
            title: optionTitle,
            description: optionDescription,
            required: optionRequired,
            sensitive: optionSensitive,
        };
        // Add default value if not required
        if (!optionRequired) {
            let defaultValue;
            if (optionType === "boolean") {
                defaultValue = await confirm({
                    message: "Default value:",
                    default: false,
                });
            }
            else if (optionType === "number") {
                const defaultStr = await input({
                    message: "Default value (number):",
                    validate: (value) => {
                        if (!value.trim())
                            return true;
                        return !isNaN(Number(value)) || "Must be a valid number";
                    },
                });
                defaultValue = defaultStr ? Number(defaultStr) : undefined;
            }
            else {
                defaultValue = await input({
                    message: "Default value (optional):",
                });
            }
            if (defaultValue !== undefined && defaultValue !== "") {
                option.default = defaultValue;
            }
        }
        // Add constraints for number types
        if (optionType === "number") {
            const addConstraints = await confirm({
                message: "Add min/max constraints?",
                default: false,
            });
            if (addConstraints) {
                const min = await input({
                    message: "Minimum value (optional):",
                    validate: (value) => {
                        if (!value.trim())
                            return true;
                        return !isNaN(Number(value)) || "Must be a valid number";
                    },
                });
                const max = await input({
                    message: "Maximum value (optional):",
                    validate: (value) => {
                        if (!value.trim())
                            return true;
                        return !isNaN(Number(value)) || "Must be a valid number";
                    },
                });
                if (min)
                    option.min = Number(min);
                if (max)
                    option.max = Number(max);
            }
        }
        userConfig[optionKey] = option;
        addMore = await confirm({
            message: "Add another configuration option?",
            default: false,
        });
    }
    return userConfig;
}
export function buildManifest(basicInfo, longDescription, authorInfo, urls, visualAssets, serverConfig, tools, toolsGenerated, prompts, promptsGenerated, compatibility, userConfig, optionalFields) {
    const { name, displayName, version, description, authorName } = basicInfo;
    const { authorEmail, authorUrl } = authorInfo;
    const { serverType, entryPoint, mcp_config } = serverConfig;
    const { keywords, license, repository } = optionalFields;
    return {
        manifest_version: CURRENT_MANIFEST_VERSION,
        name,
        ...(displayName && displayName !== name
            ? { display_name: displayName }
            : {}),
        version,
        description,
        ...(longDescription ? { long_description: longDescription } : {}),
        author: {
            name: authorName,
            ...(authorEmail ? { email: authorEmail } : {}),
            ...(authorUrl ? { url: authorUrl } : {}),
        },
        ...(urls.homepage ? { homepage: urls.homepage } : {}),
        ...(urls.documentation ? { documentation: urls.documentation } : {}),
        ...(urls.support ? { support: urls.support } : {}),
        ...(visualAssets.icon ? { icon: visualAssets.icon } : {}),
        ...(visualAssets.screenshots.length > 0
            ? { screenshots: visualAssets.screenshots }
            : {}),
        server: {
            type: serverType,
            entry_point: entryPoint,
            mcp_config,
        },
        ...(tools.length > 0 ? { tools } : {}),
        ...(toolsGenerated ? { tools_generated: true } : {}),
        ...(prompts.length > 0 ? { prompts } : {}),
        ...(promptsGenerated ? { prompts_generated: true } : {}),
        ...(compatibility ? { compatibility } : {}),
        ...(Object.keys(userConfig).length > 0 ? { user_config: userConfig } : {}),
        ...(keywords
            ? {
                keywords: keywords
                    .split(",")
                    .map((k) => k.trim())
                    .filter((k) => k),
            }
            : {}),
        ...(license ? { license } : {}),
        ...(repository ? { repository } : {}),
    };
}
export function printNextSteps() {
    console.log("\nNext steps:");
    console.log(`1. Ensure all your production dependencies are in this directory`);
    console.log(`2. Run 'mcpb pack' to create your .mcpb file`);
}
export async function initExtension(targetPath = process.cwd(), nonInteractive = false) {
    const resolvedPath = resolve(targetPath);
    const manifestPath = join(resolvedPath, "manifest.json");
    if (existsSync(manifestPath)) {
        if (nonInteractive) {
            console.log("manifest.json already exists. Use --force to overwrite in non-interactive mode.");
            return false;
        }
        const overwrite = await confirm({
            message: "manifest.json already exists. Overwrite?",
            default: false,
        });
        if (!overwrite) {
            console.log("Cancelled");
            return false;
        }
    }
    if (!nonInteractive) {
        console.log("This utility will help you create a manifest.json file for your MCPB bundle.");
        console.log("Press ^C at any time to quit.\n");
    }
    else {
        console.log("Creating manifest.json with default values...");
    }
    try {
        const packageData = readPackageJson(resolvedPath);
        // Prompt for all information or use defaults
        const basicInfo = nonInteractive
            ? getDefaultBasicInfo(packageData, resolvedPath)
            : await promptBasicInfo(packageData, resolvedPath);
        const longDescription = nonInteractive
            ? undefined
            : await promptLongDescription(basicInfo.description);
        const authorInfo = nonInteractive
            ? getDefaultAuthorInfo(packageData)
            : await promptAuthorInfo(packageData);
        const urls = nonInteractive
            ? { homepage: "", documentation: "", support: "" }
            : await promptUrls();
        const visualAssets = nonInteractive
            ? { icon: "", screenshots: [] }
            : await promptVisualAssets();
        const serverConfig = nonInteractive
            ? getDefaultServerConfig(packageData)
            : await promptServerConfig(packageData);
        const toolsData = nonInteractive
            ? { tools: [], toolsGenerated: false }
            : await promptTools();
        const promptsData = nonInteractive
            ? { prompts: [], promptsGenerated: false }
            : await promptPrompts();
        const compatibility = nonInteractive
            ? undefined
            : await promptCompatibility(serverConfig.serverType);
        const userConfig = nonInteractive ? {} : await promptUserConfig();
        const optionalFields = nonInteractive
            ? getDefaultOptionalFields(packageData)
            : await promptOptionalFields(packageData);
        // Build manifest
        const manifest = buildManifest(basicInfo, longDescription, authorInfo, urls, visualAssets, serverConfig, toolsData.tools, toolsData.toolsGenerated, promptsData.prompts, promptsData.promptsGenerated, compatibility, userConfig, optionalFields);
        // Write manifest
        writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
        console.log(`\nCreated manifest.json at ${manifestPath}`);
        printNextSteps();
        return true;
    }
    catch (error) {
        if (error instanceof Error && error.message.includes("User force closed")) {
            console.log("\nCancelled");
            return false;
        }
        throw error;
    }
}
