/**
 * Mock Debug Adapter implementation for testing
 *
 * Provides a fully functional debug adapter that simulates debugging
 * without requiring external dependencies.
 *
 * @since 2.0.0
 */
import { EventEmitter } from 'events';
import * as path from 'path';
import { AdapterState, DebugFeature, AdapterError, AdapterErrorCode } from '../debug-adapter-interface.js';
import { DebugLanguage } from '../../session/models.js';
/**
 * Mock error scenarios
 */
export var MockErrorScenario;
(function (MockErrorScenario) {
    MockErrorScenario["NONE"] = "none";
    MockErrorScenario["EXECUTABLE_NOT_FOUND"] = "executable_not_found";
    MockErrorScenario["ADAPTER_CRASH"] = "adapter_crash";
    MockErrorScenario["CONNECTION_TIMEOUT"] = "connection_timeout";
    MockErrorScenario["INVALID_BREAKPOINT"] = "invalid_breakpoint";
    MockErrorScenario["SCRIPT_ERROR"] = "script_error";
    MockErrorScenario["OUT_OF_MEMORY"] = "out_of_memory";
})(MockErrorScenario || (MockErrorScenario = {}));
/**
 * Valid state transitions
 * Made more permissive to match real adapter behavior (e.g., Python adapter)
 * Real adapters don't have strict state validation, so the mock shouldn't either
 */
const VALID_TRANSITIONS = {
    [AdapterState.UNINITIALIZED]: [
        AdapterState.INITIALIZING,
        AdapterState.READY, // Allow direct ready
        AdapterState.CONNECTED, // Allow direct connection
        AdapterState.DEBUGGING, // Allow direct debugging (matches real adapter behavior)
        AdapterState.ERROR
    ],
    [AdapterState.INITIALIZING]: [
        AdapterState.READY,
        AdapterState.CONNECTED, // Allow direct connection during init
        AdapterState.ERROR,
        AdapterState.UNINITIALIZED // Allow reset
    ],
    [AdapterState.READY]: [
        AdapterState.CONNECTED,
        AdapterState.DEBUGGING, // Allow direct debugging from ready
        AdapterState.DISCONNECTED, // Allow disconnection
        AdapterState.ERROR,
        AdapterState.UNINITIALIZED // Allow reset
    ],
    [AdapterState.CONNECTED]: [
        AdapterState.DEBUGGING,
        AdapterState.CONNECTED, // Allow staying connected (idempotent)
        AdapterState.DISCONNECTED,
        AdapterState.READY, // Allow going back to ready
        AdapterState.ERROR
    ],
    [AdapterState.DEBUGGING]: [
        AdapterState.DEBUGGING, // Allow staying in debugging (idempotent)
        AdapterState.CONNECTED, // Allow going back to connected
        AdapterState.DISCONNECTED,
        AdapterState.READY, // Allow going back to ready
        AdapterState.ERROR
    ],
    [AdapterState.DISCONNECTED]: [
        AdapterState.READY,
        AdapterState.CONNECTED, // Allow reconnection
        AdapterState.UNINITIALIZED, // Allow full reset
        AdapterState.ERROR
    ],
    [AdapterState.ERROR]: [
        AdapterState.UNINITIALIZED,
        AdapterState.READY, // Allow recovery to ready
        AdapterState.DISCONNECTED // Allow recovery to disconnected
    ]
};
/**
 * Mock debug adapter implementation
 */
export class MockDebugAdapter extends EventEmitter {
    language = DebugLanguage.MOCK;
    name = 'Mock Debug Adapter';
    state = AdapterState.UNINITIALIZED;
    config;
    dependencies;
    // State
    currentThreadId = null;
    connected = false;
    // Error simulation
    errorScenario = MockErrorScenario.NONE;
    constructor(dependencies, config = {}) {
        super();
        this.dependencies = dependencies;
        this.config = {
            defaultDelay: config.defaultDelay ?? 0,
            connectionDelay: config.connectionDelay ?? 50,
            stepDelay: config.stepDelay ?? 5,
            supportedFeatures: config.supportedFeatures ?? [
                DebugFeature.CONDITIONAL_BREAKPOINTS,
                DebugFeature.FUNCTION_BREAKPOINTS,
                DebugFeature.VARIABLE_PAGING,
                DebugFeature.SET_VARIABLE
            ],
            maxVariableDepth: config.maxVariableDepth ?? 10,
            maxArrayLength: config.maxArrayLength ?? 100,
            errorProbability: config.errorProbability ?? 0,
            errorScenarios: config.errorScenarios ?? [],
            cpuIntensive: config.cpuIntensive ?? false,
            memoryIntensive: config.memoryIntensive ?? false
        };
    }
    // ===== Lifecycle Management =====
    async initialize() {
        this.transitionTo(AdapterState.INITIALIZING);
        try {
            // Validate environment
            const validation = await this.validateEnvironment();
            if (!validation.valid) {
                this.transitionTo(AdapterState.ERROR);
                throw new AdapterError(validation.errors[0]?.message || 'Validation failed', AdapterErrorCode.ENVIRONMENT_INVALID);
            }
            this.transitionTo(AdapterState.READY);
            this.emit('initialized');
        }
        catch (error) {
            this.transitionTo(AdapterState.ERROR);
            throw error;
        }
    }
    async dispose() {
        this.currentThreadId = null;
        this.connected = false;
        this.state = AdapterState.UNINITIALIZED;
        this.emit('disposed');
    }
    // ===== State Management =====
    getState() {
        return this.state;
    }
    isReady() {
        return this.state === AdapterState.READY ||
            this.state === AdapterState.CONNECTED ||
            this.state === AdapterState.DEBUGGING;
    }
    getCurrentThreadId() {
        return this.currentThreadId;
    }
    transitionTo(newState) {
        const oldState = this.state;
        const validTransitions = VALID_TRANSITIONS[oldState];
        if (!validTransitions?.includes(newState)) {
            throw new AdapterError(`Invalid state transition: ${oldState} → ${newState}`, AdapterErrorCode.UNKNOWN_ERROR);
        }
        this.state = newState;
        this.emit('stateChanged', oldState, newState);
    }
    // ===== Environment Validation =====
    async validateEnvironment() {
        if (this.errorScenario === MockErrorScenario.EXECUTABLE_NOT_FOUND) {
            return {
                valid: false,
                errors: [{
                        code: 'MOCK_NOT_FOUND',
                        message: 'Mock executable not found',
                        recoverable: false
                    }],
                warnings: []
            };
        }
        // Mock adapter always validates successfully
        return {
            valid: true,
            errors: [],
            warnings: []
        };
    }
    getRequiredDependencies() {
        // Mock adapter has no external dependencies
        return [];
    }
    // ===== Executable Management =====
    async resolveExecutablePath(preferredPath) {
        if (preferredPath) {
            return preferredPath;
        }
        // Use node as the mock executable
        return process.execPath;
    }
    getDefaultExecutableName() {
        return 'node';
    }
    getExecutableSearchPaths() {
        return process.env.PATH?.split(path.delimiter) || [];
    }
    // ===== Adapter Configuration =====
    buildAdapterCommand(config) {
        // Get the directory of this module
        // When compiled, this will be in dist/adapters/mock/
        let mockAdapterPath;
        try {
            // Try to use import.meta.url if available
            const currentFileUrl = new URL(import.meta.url);
            let currentDir = path.dirname(currentFileUrl.pathname);
            // In Windows, remove the leading slash from the pathname
            if (process.platform === 'win32' && currentDir.startsWith('/')) {
                currentDir = currentDir.substring(1);
            }
            // Decode URL encoding (e.g., %20 for spaces)
            currentDir = decodeURIComponent(currentDir);
            mockAdapterPath = path.join(currentDir, 'mock-adapter-process.js');
        }
        catch {
            // Fallback: assume we're running from the project root
            // The compiled files are in dist/adapters/mock/
            const projectRoot = path.resolve(process.cwd());
            mockAdapterPath = path.join(projectRoot, 'dist', 'adapters', 'mock', 'mock-adapter-process.js');
            this.dependencies.logger?.debug(`[MockDebugAdapter] Using fallback path resolution: ${mockAdapterPath}`);
        }
        return {
            command: process.execPath,
            args: [
                mockAdapterPath,
                '--port', config.adapterPort.toString(),
                '--host', config.adapterHost,
                '--session', config.sessionId
            ],
            env: {
                ...process.env,
                MOCK_ADAPTER_LOG: config.logDir
            }
        };
    }
    getAdapterModuleName() {
        return 'mock-adapter';
    }
    getAdapterInstallCommand() {
        return 'echo "Mock adapter is built-in"';
    }
    // ===== Debug Configuration =====
    transformLaunchConfig(config) {
        return {
            ...config,
            type: 'mock',
            request: 'launch',
            name: 'Mock Debug'
        };
    }
    getDefaultLaunchConfig() {
        return {
            stopOnEntry: false,
            justMyCode: true,
            env: {},
            cwd: process.cwd()
        };
    }
    // ===== DAP Protocol Operations =====
    async sendDapRequest(command, args) {
        // This will be handled by ProxyManager
        // Mock adapter just validates the request is appropriate
        this.dependencies.logger?.debug(`[MockDebugAdapter] DAP request: ${command}`, args);
        // ProxyManager will handle actual communication
        return {};
    }
    handleDapEvent(event) {
        // Update thread ID on stopped events
        if (event.event === 'stopped' && event.body?.threadId) {
            this.currentThreadId = event.body.threadId;
            this.transitionTo(AdapterState.DEBUGGING);
        }
        else if (event.event === 'continued') {
            this.transitionTo(AdapterState.DEBUGGING);
        }
        else if (event.event === 'terminated' || event.event === 'exited') {
            this.currentThreadId = null;
            if (this.connected) {
                this.transitionTo(AdapterState.CONNECTED);
            }
        }
        this.emit(event.event, event.body);
    }
    handleDapResponse(_response) {
        // Mock adapter doesn't need special response handling
        void _response; // Explicitly ignore
    }
    // ===== Connection Management =====
    async connect(host, port) {
        // Simulate connection delay if configured
        if (this.config.connectionDelay > 0) {
            await new Promise(resolve => setTimeout(resolve, this.config.connectionDelay));
        }
        if (this.errorScenario === MockErrorScenario.CONNECTION_TIMEOUT) {
            throw new AdapterError('Connection timeout', AdapterErrorCode.CONNECTION_TIMEOUT, true);
        }
        // Connection is handled by ProxyManager
        // Store connection info for debugging purposes
        this.dependencies.logger?.debug(`[MockDebugAdapter] Connect request to ${host}:${port}`);
        this.connected = true;
        this.transitionTo(AdapterState.CONNECTED);
        this.emit('connected');
    }
    async disconnect() {
        this.connected = false;
        this.currentThreadId = null;
        this.transitionTo(AdapterState.DISCONNECTED);
        this.emit('disconnected');
    }
    isConnected() {
        return this.connected;
    }
    // ===== Error Handling =====
    getInstallationInstructions() {
        return 'The Mock Debug Adapter is built-in and requires no installation.';
    }
    getMissingExecutableError() {
        return 'Mock executable not found. This should not happen with the mock adapter.';
    }
    translateErrorMessage(error) {
        if (error.message.includes('ENOENT')) {
            return 'Mock file not found: ' + error.message;
        }
        return error.message;
    }
    // ===== Feature Support =====
    supportsFeature(feature) {
        return this.config.supportedFeatures?.includes(feature) || false;
    }
    getFeatureRequirements(feature) {
        const requirements = [];
        if (feature === DebugFeature.CONDITIONAL_BREAKPOINTS) {
            requirements.push({
                type: 'version',
                description: 'Mock adapter version 1.0+',
                required: true
            });
        }
        return requirements;
    }
    getCapabilities() {
        return {
            supportsConfigurationDoneRequest: true,
            supportsFunctionBreakpoints: this.supportsFeature(DebugFeature.FUNCTION_BREAKPOINTS),
            supportsConditionalBreakpoints: this.supportsFeature(DebugFeature.CONDITIONAL_BREAKPOINTS),
            supportsHitConditionalBreakpoints: false,
            supportsEvaluateForHovers: this.supportsFeature(DebugFeature.EVALUATE_FOR_HOVERS),
            exceptionBreakpointFilters: [],
            supportsStepBack: false,
            supportsSetVariable: this.supportsFeature(DebugFeature.SET_VARIABLE),
            supportsRestartFrame: false,
            supportsGotoTargetsRequest: false,
            supportsStepInTargetsRequest: false,
            supportsCompletionsRequest: false,
            supportsModulesRequest: false,
            supportsRestartRequest: false,
            supportsExceptionOptions: false,
            supportsValueFormattingOptions: false,
            supportsExceptionInfoRequest: false,
            supportTerminateDebuggee: true,
            supportSuspendDebuggee: false,
            supportsDelayedStackTraceLoading: false,
            supportsLoadedSourcesRequest: false,
            supportsLogPoints: this.supportsFeature(DebugFeature.LOG_POINTS),
            supportsTerminateThreadsRequest: false,
            supportsSetExpression: false,
            supportsTerminateRequest: true,
            supportsDataBreakpoints: false,
            supportsReadMemoryRequest: false,
            supportsWriteMemoryRequest: false,
            supportsDisassembleRequest: false,
            supportsCancelRequest: false,
            supportsBreakpointLocationsRequest: false,
            supportsClipboardContext: false,
            supportsSteppingGranularity: false,
            supportsInstructionBreakpoints: false,
            supportsExceptionFilterOptions: false,
            supportsSingleThreadExecutionRequests: false
        };
    }
    // ===== Mock-specific methods =====
    /**
     * Set error scenario for testing
     */
    setErrorScenario(scenario) {
        this.errorScenario = scenario;
    }
}
//# sourceMappingURL=mock-debug-adapter.js.map