import * as http from "http";
import { z } from "zod";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import CreatorTools from "../app/CreatorTools";
import ServerManager from "./ServerManager";
import LocalEnvironment from "./LocalEnvironment";
import { IWorldSettings } from "../minecraft/IWorldSettings";
import IStatus from "../app/Status";
import IActionSetData from "../actions/IActionSetData";
import IVector3 from "../minecraft/IVector3";
import Project from "../app/Project";
import { ModelTemplateType } from "../minecraft/ModelDesignTemplates";
import { MinecraftContentSchema } from "../minecraft/ContentMetaSchemaZod";
/**
 * Interface for MCT MCP preferences that can be stored in .mct/mcp/prefs.json files.
 * These preferences control security and feature flags for MCP operations.
 */
export interface IMctMcpPrefs {
    /** If true, allows the readImageFile tool to read image files from this folder and its subfolders */
    allowImageFileReadsInDescendentFolders?: boolean;
    /** If true, allows the writeImageFile tool to write image files to this folder and its subfolders */
    allowImageFileWritesInDescendentFolders?: boolean;
}
export default class MinecraftMcpServer {
    /** Starting port for the internal HTTP server range */
    private static readonly PORT_RANGE_START;
    /** Ending port for the internal HTTP server range (200 ports available) */
    private static readonly PORT_RANGE_END;
    /** Maximum attempts to find an available port before giving up */
    private static readonly PORT_MAX_ATTEMPTS;
    private _server;
    private _env;
    /** Single HTTP transport instance. Created once in startHttp() and reused for all requests. */
    private _httpTransport;
    private _creatorTools;
    private _serverManager;
    /** Cache for loaded MCP preferences, keyed by folder path where prefs.json was found */
    private _mcpPrefsCache;
    /** Folders we've already checked and found no prefs.json (negative cache) */
    private _mcpPrefsNotFoundFolders;
    /** HTTP server port for model preview rendering (dynamically assigned on startup) */
    private _previewServerPort;
    /**
     * Cached PlaywrightPageRenderer for reuse across preview operations.
     *
     * TODO: The renderer init/health-check/reinit boilerplate is duplicated ~4 times
     * in this file (preview_model, preview_volume, preview_structure, screenshot).
     * Extract into a shared `ensureRendererReady(baseUrl, httpServer)` method.
     */
    private _cachedRenderer;
    /** Flag to prevent multiple cleanup calls */
    private _cleaningUp;
    /**
     * Maps user-facing session names to BDS slot numbers.
     * The "default" session is auto-registered when `mct serve` starts BDS on slot 0.
     * Additional sessions can be created via createMinecraftSessionWithContent or
     * connected to existing slots via connectToMinecraftSession.
     */
    private _sessions;
    /**
     * Working folder path for MCP operations.
     * When set via the -i argument, this folder is used as the default context for
     * file operations and is exposed to AI assistants via the MCP protocol's prompts.
     */
    private _workingFolder;
    /** Getter for the working folder */
    get workingFolder(): string | undefined;
    constructor();
    /**
     * Wrapper for McpServer.registerTool that avoids TS2589 "Type instantiation is
     * excessively deep and possibly infinite" caused by the SDK's complex generic
     * inference on ToolCallback<InputArgs>. Casts the callback to `any` to break the
     * recursive type chain while preserving runtime behavior.
     */
    private _registerTool;
    /**
     * Ensures a ServerManager instance is available, creating one if needed.
     * If a ServerManager was provided externally (e.g., from HttpServer in `mct serve` mode),
     * it will be reused. Otherwise, a new one is created with the "mcp" slot prefix
     * to isolate MCP servers from other contexts.
     */
    private ensureServerManager;
    /**
     * Resolve a session name to a slot number.
     * - If sessionName is empty/undefined or "default", returns slot 0 (the default session).
     * - Otherwise, looks up the session in the registered sessions map.
     * - Throws if the session name is not found.
     */
    private _resolveSlot;
    /**
     * Handle an incoming HTTP request by delegating to the single transport.
     *
     * Architecture: We use one StreamableHTTPServerTransport created in startHttp()
     * and connected once to the McpServer. The transport handles session management,
     * initialization, and request routing internally. This avoids the SDK limitation
     * where registerCapabilities() cannot be called after connect().
     *
     * For POST requests, we pre-parse the body since Node's http.IncomingMessage
     * doesn't auto-parse JSON (unlike Express). The parsed body is passed to
     * transport.handleRequest() so it doesn't try to re-parse.
     */
    handleRequest(req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>): Promise<void>;
    sendErrorRequest(statusCode: number, message: string, req: http.IncomingMessage, res: http.ServerResponse): void;
    /**
     * Finds MCP preferences for a given file path by looking for .mct/mcp/prefs.json
     * in the file's parent folder and up to 11 levels of parent folders.
     * Results are cached per session to avoid redundant file system reads.
     *
     * @param filePath The absolute path to the file being accessed
     * @returns The MCP preferences if found, or undefined if no prefs.json exists`
     */
    getMcpPrefsForPath(filePath: string): IMctMcpPrefs | undefined;
    /**
     * Checks if a specific MCP preference flag is enabled for a given file path.
     *
     * @param filePath The absolute path to the file being accessed
     * @param prefKey The preference key to check
     * @returns true if the preference is explicitly set to true, false otherwise
     */
    isMcpPrefEnabled(filePath: string, prefKey: keyof IMctMcpPrefs): boolean;
    /**
     * Validates that a file path is safe for MCP operations.
     * Ensures the path doesn't use traversal sequences and that the resolved path
     * stays within the directory tree authorized by the prefs.json file.
     */
    private _validateMcpFilePath;
    /**
     * Validates an MCP file path and returns an error CallToolResult if invalid,
     * or undefined if the path is safe. Use as an early-return guard in MCP tool handlers.
     */
    private _checkMcpFilePath;
    _processValidateContent(args: {
        jsonContentOrBase64ZipContent: string;
    }): Promise<CallToolResult>;
    _sessionOp(args: {
        sessionName: string;
    }): Promise<CallToolResult>;
    /**
     * Lists all Minecraft sessions — both registered named sessions and any
     * active BDS slots discovered via the ServerManager.
     */
    _listMinecraftSessionsOp(): Promise<CallToolResult>;
    /**
     * Registers an existing BDS slot as a named session so that subsequent
     * tool calls can reference it by name.
     */
    _connectToMinecraftSessionOp(args: {
        sessionName: string;
        slot?: number;
    }): Promise<CallToolResult>;
    _moveSessionPlayerToLocation(args: {
        sessionName: string;
        playerName: string;
        locationToHavePlayerMoveTo: IVector3;
    }): Promise<CallToolResult>;
    _createMinecraftSession(args: {
        sessionName: string;
        packagedMcaddonOrMcworldFilePath: string;
        testPlayerNameToUse: string;
    }): Promise<CallToolResult>;
    _runActionSet(actionSet: IActionSetData, slot?: number): Promise<any>;
    getWorldSettings(): IWorldSettings;
    _runActionSetOp(args: {
        actionSet?: any;
        sessionName?: string;
    }): Promise<CallToolResult>;
    _runCommandOp(args: {
        sessionName: string;
        command: string;
    }): Promise<CallToolResult>;
    runCommand(command: string, token?: string, slot?: number): Promise<string | undefined>;
    _processValidateContentAtPath(args: {
        filePath: string;
    }): Promise<CallToolResult>;
    _create(project: Project, title: string, description: string, newName: string, creator: string, template: string): Promise<void>;
    _add(project: Project, templateType: string, newName: string): Promise<boolean>;
    _createOp(args: {
        folderPathToCreateProjectAt: string;
        title: string;
        description?: string;
        newName: string;
        creator: string;
        template: "addonStarter" | "tsStarter" | "addonFull" | "scriptBox" | "dlStarter" | "editor-scriptBox" | "editor-basics";
    }): Promise<CallToolResult>;
    _addOp(args: {
        folderPathToCreateProjectAt: string;
        templateType: "basicUnitCubeBlock" | "crateBlock" | "basicDieBlock" | "sushiRollBlock" | "fishBowlBlock" | "hardBiscuit" | "pear" | "elixir" | "rod" | "key" | "customSword" | "wrench" | "allay" | "axolotl" | "cat" | "cow" | "creeper" | "enderman" | "rabbit" | "pig" | "sheep" | "skeleton" | "wolf" | "zombie" | "spawn_rule" | "loot_table" | "recipe_shapeless" | "recipe_shaped" | "feature_rule" | "jigsaw" | "atmospherics" | "color_grading" | "lighting" | "pbr" | "biome_behavior" | "entity_behavior" | "entity_resources" | "item_behavior" | "attachable" | "block_behavior" | "block_culling" | "block_catalog" | "biome_resource" | "aggregate_feature" | "animation" | "animation_controller" | "render_controller";
        name: string;
    }): Promise<CallToolResult>;
    /**
     * Creates Minecraft content from a meta-schema definition.
     * This is a simplified, AI-friendly format that generates all required files.
     */
    _createMinecraftContentOp(args: {
        definition: z.infer<typeof MinecraftContentSchema>;
        outputPath: string;
    }): Promise<CallToolResult>;
    /**
     * Finds the first existing pack folder inside a container directory (e.g., behavior_packs/).
     * Returns the folder path if a pack (folder with manifest.json) is found, otherwise undefined.
     */
    private static _findExistingPackFolder;
    /**
     * Returns true when the supplied folder already looks like a Bedrock resource
     * pack — i.e. it contains a top-level manifest.json whose modules include a
     * `resources` module. Used by designModel to avoid creating a nested
     * `resource_packs/` subdirectory inside an RP that the caller passed
     * directly, which previously caused a recurring "files written to the wrong
     * path" symptom.
     */
    private static _isResourcePackFolder;
    /**
     * Sanitize a display name or namespace into a safe folder name.
     * Lower-cases, replaces non-alphanumeric runs with '_', trims.
     * Caps length at 64 characters to keep resulting paths well under the
     * Windows MAX_PATH limit once nested inside the project/pack hierarchy.
     */
    private static _toSafeFolderName;
    /**
     * Returns the list of visible child entries in a directory, or an empty array if
     * the directory doesn't exist / can't be read. Hidden/system files (.git, .DS_Store,
     * Thumbs.db, desktop.ini) are ignored when deciding whether a folder is "empty".
     */
    private static _visibleChildren;
    /**
     * Resolves where content should actually be generated, given a user-provided outputPath.
     *
     * Heuristic (in priority order):
     *   1. If outputPath (or a parent within 2 levels) has a Minecraft project reference point
     *      — package.json, behavior_packs/, resource_packs/, or a manifest.json — anchor to
     *      that project root. This is the most common "add to existing project" case.
     *   2. If outputPath doesn't exist, or is empty (ignoring hidden/system files), use it
     *      directly as the project root.
     *   3. Otherwise (outputPath is non-empty with unrelated content), create a subfolder
     *      named after the namespace/displayName and use that as the root.
     *
     * Returns both the resolved root and a human-readable reason string (useful for the
     * tool response so agents learn where files landed and why).
     */
    private static _resolveProjectRoot;
    /**
     * Writes a manifest.json file, but preserves the UUIDs from any existing manifest
     * at the same location. This prevents breaking worlds that already reference the pack
     * when content is added across multiple MCP calls.
     */
    private static _writeManifestPreservingUuids;
    /**
     * Writes a singleton JSON file (terrain_texture.json, item_texture.json, blocks.json,
     * sound_definitions.json, music_definitions.json, etc.), deep-merging with any existing
     * data so that previously-added entries are preserved instead of being overwritten.
     *
     * Merge strategy:
     * - Recursively merges object keys: new entries win on conflict, but existing
     *   object-valued entries are recursively merged rather than replaced.
     * - Scalar top-level keys from the existing file are preserved if absent in the new content.
     * - This handles texture_data in terrain/item_texture.json, block entries in blocks.json,
     *   entity_sounds.entities in sounds.json, sound_definitions entries, etc.
     */
    private static _writeSingletonJsonMerging;
    /**
     * Gets the effective content schema for an existing Minecraft project.
     * This analyzes the project's entities, blocks, items, etc. and infers what
     * traits and simplified properties would represent them in meta-schema format.
     *
     * This is the inverse of createMinecraftContentOp - instead of generating
     * native content from a schema, it analyzes native content to produce a schema.
     */
    _getEffectiveContentSchemaOp(args: {
        folderPath: string;
        options?: {
            minTraitConfidence?: number;
            includeRawComponents?: boolean;
            inferNamespace?: boolean;
            includeBehaviorPresets?: boolean;
            includeComponentGroups?: boolean;
            includeEvents?: boolean;
        };
    }): Promise<CallToolResult>;
    /**
     * Reads an image file and returns its contents as base64-encoded image data
     * that can be displayed by the AI.
     *
     * Requires allowImageFileReadsInDescendentFolders to be set to true in
     * .mct/mcp/prefs.json in the file's parent folder or up to 3 levels above.
     */
    _readImageFileOp(args: {
        filePath: string;
    }): Promise<CallToolResult>;
    /**
     * Extract dimensions from a JPEG buffer by scanning for SOF markers.
     * Returns a "widthxheight" string, or empty string if extraction fails.
     */
    private static _getJpegDimensions;
    /**
     * Maximum base64 size for images returned from MCP tools to stay safely under
     * API request limits (e.g., Claude's ~4MB total request limit).
     * Base64 encoding inflates binary data by ~33%, and we need headroom for the rest
     * of the request payload (tool definitions, conversation history, etc.), so we
     * cap individual images at ~1.5MB base64. This is more conservative than the 3MB
     * limit to account for conversations with multiple images.
     */
    private static readonly MAX_IMAGE_BASE64_BYTES;
    /**
     * Ensure a base64-encoded image fits within AI context limits.
     * If the image is too large, it will be downscaled and re-encoded as PNG.
     *
     * This should be called before returning any image from an MCP tool to prevent
     * 413 Request Entity Too Large errors from the AI backend.
     *
     * @param base64Data Base64-encoded image data
     * @param mimeType MIME type of the image
     * @returns Object with (possibly downscaled) base64 data and mimeType. The mimeType
     *          may change to "image/png" if the image was re-encoded.
     */
    static ensureImageFitsContext(base64Data: string, mimeType: string): {
        base64: string;
        mimeType: string;
        wasDownscaled: boolean;
    };
    /**
     * Downscale an image buffer so its base64 representation fits within maxBase64Bytes.
     * Decodes the image to RGBA pixels, computes a scale factor, resizes using bilinear
     * interpolation, and re-encodes as PNG.
     *
     * @param imageBuffer Raw image file bytes (PNG, JPEG, etc.)
     * @param mimeType MIME type of the source image
     * @param maxBase64Bytes Maximum allowed base64 string length
     * @returns Object with base64 string, or undefined if downscaling fails
     */
    private static _downscaleImageToFit;
    /**
     * Resize RGBA pixel data using bilinear interpolation.
     * Produces smoother results than nearest-neighbor for downscaling screenshots and textures.
     *
     * @param srcPixels Source RGBA pixel data (4 bytes per pixel)
     * @param srcW Source width
     * @param srcH Source height
     * @param dstW Destination width
     * @param dstH Destination height
     * @returns Resized RGBA pixel data
     */
    private static _bilinearResize;
    /**
     * Writes base64-encoded image data to a file.
     *
     * Requires allowImageFileWritesInDescendentFolders to be set to true in
     * .mct/mcp/prefs.json in the file's parent folder or up to 3 levels above.
     */
    _writeImageFileFromBase64Op(args: {
        filePath: string;
        base64Data: string;
        mimeType?: string;
    }): Promise<CallToolResult>;
    /**
     * Converts SVG markup to PNG and writes it to a file.
     *
     * Requires allowImageFileWritesInDescendentFolders to be set to true in
     * .mct/mcp/prefs.json in the file's parent folder or up to 3 levels above.
     */
    _writeImageFileFromSvgOp(args: {
        filePath: string;
        svgContent: string;
        width?: number;
        height?: number;
    }): Promise<CallToolResult>;
    /**
     * Writes an image file from a pixel art definition with paletted pixels.
     *
     * The pixel art format uses ASCII-style lines where each character maps to a color
     * in a palette. Spaces are transparent. This is ideal for creating Minecraft-style
     * pixel art textures.
     *
     * Requires allowImageFileWritesInDescendentFolders to be set to true in
     * .mct/mcp/prefs.json in the file's parent folder or up to 3 levels above.
     */
    _writeImageFileFromPixelArtOp(args: {
        filePath: string;
        lines: string[];
        palette: {
            [char: string]: {
                r?: number;
                g?: number;
                b?: number;
                a?: number;
                hex?: string;
            };
        };
        scale?: number;
        backgroundColor?: {
            r?: number;
            g?: number;
            b?: number;
            a?: number;
            hex?: string;
        };
    }): Promise<CallToolResult>;
    /**
     * Preview a model design by converting it to geometry and rendering a preview image.
     * Returns the preview as a base64-encoded PNG image.
     */
    _previewModelDesignOp(args: any): Promise<CallToolResult>;
    /**
     * Export a model design to .geo.json and texture.png files.
     */
    _exportModelDesignOp(args: any): Promise<CallToolResult>;
    /**
     * Unified tool: Creates a 3D model, exports files to project, and returns a preview.
     *
     * This combines the functionality of previewModelDesign and exportModelDesign into
     * a single, project-aware operation that:
     * 1. Validates and converts the design to geometry + texture
     * 2. Saves files to the appropriate project folder (auto-detected)
     * 3. Persists the design to an accessory folder for future iteration
     * 4. Auto-wires to matching entity/block/item if found
     * 5. Returns a preview image
     */
    _designModelOp(args: any): Promise<CallToolResult>;
    /**
     * Unified tool for building structures in Minecraft projects.
     * Combines structure preview + export + project integration into one step.
     */
    _designStructureOp(args: any): Promise<CallToolResult>;
    /**
     * Returns starter model templates for common Minecraft entity types.
     * These provide proper Minecraft-scale geometry with blocky pixel-art style textures.
     */
    _getModelTemplatesOp(args: {
        templateType: ModelTemplateType;
    }): Promise<CallToolResult>;
    /**
     * Validates an IBlockVolume has the required basic structure.
     * Size is now optional - if not provided, it will be inferred from the data.
     * String lengths and row counts don't need to match exactly - shorter strings
     * and missing rows are treated as air blocks.
     *
     * Returns an error message if validation fails, or undefined if valid.
     */
    private _validateBlockVolumeDimensions;
    /**
     * Preview a structure design (IBlockVolume) by converting it to an MCStructure and rendering a preview image.
     * Returns the preview as a base64-encoded PNG image from multiple angles.
     */
    _previewStructureDesignOp(args: any): Promise<CallToolResult>;
    /**
     * Export a structure design (IBlockVolume) to an MCStructure file.
     */
    _exportStructureDesignOp(args: any): Promise<CallToolResult>;
    /**
     * Configure MCP prompts that expose server configuration to AI assistants.
     * The main prompt is "working-folder" which tells the AI where to write content.
     */
    _configurePrompts(): void;
    _configureTools(): Promise<void>;
    /**
     * Check if a port is available for use.
     * @param port The port number to check
     * @returns Promise that resolves to true if the port is available, false otherwise
     */
    private static isPortAvailable;
    /**
     * Find an available port by randomly selecting from the configured range.
     * Excludes browser-unsafe ports that would cause ERR_UNSAFE_PORT errors.
     * @returns Promise that resolves to an available port, or a random port if none found after max attempts
     */
    private findAvailablePort;
    startStdio(creatorTools: CreatorTools, env: LocalEnvironment, workingFolder?: string): Promise<void>;
    /**
     * Clean up resources (browser, HTTP server, etc.)
     */
    cleanup(): Promise<void>;
    static handleStatusAdded(creatorTools: CreatorTools, status: IStatus): void;
    startHttp(creatorTools: CreatorTools, env: LocalEnvironment, serverManager?: ServerManager): Promise<void>;
}
