#!/usr/bin/env node
import * as path from 'path';
import * as net from 'net';
// Simple DAP connection implementation
class DAPConnection {
    input;
    output;
    messageBuffer = '';
    constructor(input = process.stdin, output = process.stdout) {
        this.input = input;
        this.output = output;
    }
    start() {
        this.input.on('data', (chunk) => {
            this.messageBuffer += chunk.toString();
            this.processMessages();
        });
    }
    on(event, handler) {
        if (event === 'request') {
            this.onRequest = handler;
        }
        else if (event === 'disconnect') {
            this.input.on('end', () => handler({}));
            this.input.on('close', () => handler({}));
        }
    }
    sendResponse(response) {
        this.sendMessage(response);
    }
    sendEvent(event) {
        this.sendMessage(event);
    }
    onRequest;
    processMessages() {
        while (true) {
            const idx = this.messageBuffer.indexOf('\r\n\r\n');
            if (idx === -1)
                break;
            const header = this.messageBuffer.substring(0, idx);
            const contentLengthMatch = header.match(/Content-Length: (\d+)/);
            if (!contentLengthMatch) {
                this.messageBuffer = this.messageBuffer.substring(idx + 4);
                continue;
            }
            const contentLength = parseInt(contentLengthMatch[1], 10);
            const messageStart = idx + 4;
            if (this.messageBuffer.length < messageStart + contentLength)
                break;
            const messageContent = this.messageBuffer.substring(messageStart, messageStart + contentLength);
            this.messageBuffer = this.messageBuffer.substring(messageStart + contentLength);
            try {
                const message = JSON.parse(messageContent);
                if (message.type === 'request' && this.onRequest) {
                    this.onRequest(message);
                }
            }
            catch {
                // Ignore parse errors
            }
        }
    }
    sendMessage(message) {
        const json = JSON.stringify(message);
        const contentLength = Buffer.byteLength(json, 'utf8');
        this.output.write(`Content-Length: ${contentLength}\r\n\r\n${json}`, 'utf8');
    }
}
function createConnection(input, output) {
    return new DAPConnection(input, output);
}
/**
 * Mock DAP server implementation
 */
class MockDebugAdapterProcess {
    connection;
    server;
    isInitialized = false;
    breakpoints = new Map();
    variableHandles = new Map();
    nextVariableReference = 1000;
    currentLine = 1;
    isRunning = false;
    threads = [{ id: 1, name: 'main' }];
    constructor() {
        // Parse command line arguments
        const args = process.argv.slice(2);
        let port;
        let host = 'localhost';
        let sessionId = 'mock-session';
        for (let i = 0; i < args.length; i++) {
            switch (args[i]) {
                case '--port':
                    port = parseInt(args[i + 1], 10);
                    i++;
                    break;
                case '--host':
                    host = args[i + 1];
                    i++;
                    break;
                case '--session':
                    sessionId = args[i + 1];
                    i++;
                    break;
            }
        }
        // Log startup
        this.log(`Mock Debug Adapter Process started - session: ${sessionId}, host: ${host}, port: ${port || 'stdio'}`);
        if (port) {
            // Set up TCP server
            this.setupTCPServer(host, port);
        }
        else {
            // Use stdio
            this.connection = createConnection();
            this.setupConnection(this.connection);
            this.connection.start();
        }
    }
    setupTCPServer(host, port) {
        this.server = net.createServer((socket) => {
            this.log(`Client connected from ${socket.remoteAddress}:${socket.remotePort}`);
            // Create connection for this socket
            this.connection = createConnection(socket, socket);
            this.setupConnection(this.connection);
            this.connection.start();
            socket.on('close', () => {
                this.log('Client socket closed');
                // Don't exit the process, allow reconnections
            });
            socket.on('error', (err) => {
                this.log(`Socket error: ${err.message}`);
            });
        });
        this.server.listen(port, host, () => {
            this.log(`TCP server listening on ${host}:${port}`);
        });
        this.server.on('error', (err) => {
            this.log(`Server error: ${err.message}`);
            process.exit(1);
        });
    }
    setupConnection(connection) {
        // Set up message handlers
        connection.on('request', this.handleRequest.bind(this));
        connection.on('disconnect', () => {
            this.log('Client disconnected');
            // For TCP connections, don't exit - allow reconnection
            if (!this.server) {
                process.exit(0);
            }
        });
    }
    log(message) {
        // Log to stderr so it doesn't interfere with protocol messages
        console.error(`[MockDAP] ${message}`);
    }
    handleRequest(request) {
        this.log(`Received request: ${request.command}`);
        switch (request.command) {
            case 'initialize':
                this.handleInitialize(request);
                break;
            case 'configurationDone':
                this.handleConfigurationDone(request);
                break;
            case 'launch':
                this.handleLaunch(request);
                break;
            case 'setBreakpoints':
                this.handleSetBreakpoints(request);
                break;
            case 'threads':
                this.handleThreads(request);
                break;
            case 'stackTrace':
                this.handleStackTrace(request);
                break;
            case 'scopes':
                this.handleScopes(request);
                break;
            case 'variables':
                this.handleVariables(request);
                break;
            case 'continue':
                this.handleContinue(request);
                break;
            case 'next':
                this.handleNext(request);
                break;
            case 'stepIn':
                this.handleStepIn(request);
                break;
            case 'stepOut':
                this.handleStepOut(request);
                break;
            case 'pause':
                this.handlePause(request);
                break;
            case 'disconnect':
                this.handleDisconnect(request);
                break;
            case 'terminate':
                this.handleTerminate(request);
                break;
            default:
                this.sendErrorResponse(request, 1000, `Unhandled command: ${request.command}`);
        }
    }
    handleInitialize(request) {
        const response = {
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: {
                supportsConfigurationDoneRequest: true,
                supportsFunctionBreakpoints: false,
                supportsConditionalBreakpoints: true,
                supportsHitConditionalBreakpoints: false,
                supportsEvaluateForHovers: true,
                exceptionBreakpointFilters: [],
                supportsStepBack: false,
                supportsSetVariable: true,
                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: false,
                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
            }
        };
        this.sendResponse(response);
        this.sendEvent({
            seq: 0,
            type: 'event',
            event: 'initialized'
        });
        this.isInitialized = true;
    }
    handleConfigurationDone(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
    }
    handleLaunch(request) {
        const args = request.arguments;
        this.log(`Launching with args: ${JSON.stringify(args)}`);
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        // If stopOnEntry is set, send a stopped event
        if (args.stopOnEntry) {
            setTimeout(() => {
                this.log(`Sending stopped event for stopOnEntry`);
                this.sendEvent({
                    seq: 0,
                    type: 'event',
                    event: 'stopped',
                    body: {
                        reason: 'entry',
                        threadId: 1,
                        allThreadsStopped: true
                    }
                });
            }, 100);
        }
        else {
            this.isRunning = true;
            this.log(`Running without stopOnEntry, will hit first breakpoint`);
            // Simulate running to first breakpoint
            setTimeout(() => {
                const allBreakpoints = Array.from(this.breakpoints.entries())
                    .flatMap(([path, bps]) => bps.map(bp => ({ path, ...bp })))
                    .filter(bp => bp.line !== undefined)
                    .sort((a, b) => (a.line || 0) - (b.line || 0));
                if (allBreakpoints.length > 0) {
                    const firstBreakpoint = allBreakpoints[0];
                    this.currentLine = firstBreakpoint.line || 1;
                    this.isRunning = false;
                    this.log(`Hit first breakpoint at line ${this.currentLine}`);
                    this.sendEvent({
                        seq: 0,
                        type: 'event',
                        event: 'stopped',
                        body: {
                            reason: 'breakpoint',
                            threadId: 1,
                            allThreadsStopped: true
                        }
                    });
                }
                else {
                    this.log(`No breakpoints set, program would run to completion`);
                    this.sendEvent({
                        seq: 0,
                        type: 'event',
                        event: 'terminated'
                    });
                    this.sendEvent({
                        seq: 0,
                        type: 'event',
                        event: 'exited',
                        body: {
                            exitCode: 0
                        }
                    });
                }
            }, 200);
        }
    }
    handleSetBreakpoints(request) {
        const args = request.arguments;
        const breakpoints = [];
        if (args.breakpoints) {
            for (const bp of args.breakpoints) {
                breakpoints.push({
                    id: Math.floor(Math.random() * 100000),
                    verified: true,
                    line: bp.line,
                    source: args.source
                });
            }
        }
        this.breakpoints.set(args.source.path || 'unknown', breakpoints);
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: {
                breakpoints
            }
        });
    }
    handleThreads(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: {
                threads: this.threads
            }
        });
    }
    handleStackTrace(request) {
        const stackFrames = [
            {
                id: 0,
                name: 'main',
                source: {
                    name: 'main.mock',
                    path: path.join(process.cwd(), 'main.mock')
                },
                line: this.currentLine,
                column: 0
            },
            {
                id: 1,
                name: 'mockFunction',
                source: {
                    name: 'lib.mock',
                    path: path.join(process.cwd(), 'lib.mock')
                },
                line: 42,
                column: 0
            }
        ];
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: {
                stackFrames,
                totalFrames: stackFrames.length
            }
        });
    }
    handleScopes(request) {
        const scopes = [
            {
                name: 'Locals',
                variablesReference: this.getOrCreateVariableReference({
                    type: 'locals',
                    variables: [
                        { name: 'x', value: '10', type: 'int' },
                        { name: 'y', value: '20', type: 'int' },
                        { name: 'result', value: '30', type: 'int' }
                    ]
                }),
                expensive: false
            },
            {
                name: 'Globals',
                variablesReference: this.getOrCreateVariableReference({
                    type: 'globals',
                    variables: [
                        { name: '__name__', value: '"__main__"', type: 'str' },
                        { name: '__file__', value: '"simple-mock.js"', type: 'str' }
                    ]
                }),
                expensive: false
            }
        ];
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: { scopes }
        });
    }
    handleVariables(request) {
        const args = request.arguments;
        const data = this.variableHandles.get(args.variablesReference);
        const variables = [];
        if (data && data.variables) {
            for (const v of data.variables) {
                variables.push({
                    name: v.name,
                    value: v.value,
                    type: v.type,
                    variablesReference: 0
                });
            }
        }
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: { variables }
        });
    }
    handleContinue(request) {
        this.isRunning = true;
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true,
            body: {
                allThreadsContinued: true
            }
        });
        // Simulate hitting a breakpoint or terminating
        setTimeout(() => {
            const allBreakpoints = Array.from(this.breakpoints.entries())
                .flatMap(([path, bps]) => bps.map(bp => ({ path, ...bp })))
                .filter(bp => bp.line !== undefined)
                .sort((a, b) => (a.line || 0) - (b.line || 0));
            this.log(`Continue from line ${this.currentLine}. All breakpoints: ${allBreakpoints.map(bp => bp.line).join(', ')}`);
            // Find next breakpoint after current line
            const nextBreakpoint = allBreakpoints.find(bp => (bp.line || 0) > this.currentLine);
            this.log(`Next breakpoint after line ${this.currentLine}: ${nextBreakpoint ? nextBreakpoint.line : 'none'}`);
            if (nextBreakpoint && nextBreakpoint.line) {
                // Hit the next breakpoint
                this.isRunning = false;
                this.currentLine = nextBreakpoint.line;
                this.log(`Stopping at breakpoint on line ${this.currentLine}`);
                this.sendEvent({
                    seq: 0,
                    type: 'event',
                    event: 'stopped',
                    body: {
                        reason: 'breakpoint',
                        threadId: 1,
                        allThreadsStopped: true
                    }
                });
            }
            else {
                // No more breakpoints - program terminated
                this.log(`No more breakpoints after line ${this.currentLine}, terminating program`);
                this.sendEvent({
                    seq: 0,
                    type: 'event',
                    event: 'terminated'
                });
                this.sendEvent({
                    seq: 0,
                    type: 'event',
                    event: 'exited',
                    body: {
                        exitCode: 0
                    }
                });
            }
        }, 200);
    }
    handleNext(request) {
        this.currentLine++;
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        setTimeout(() => {
            this.sendEvent({
                seq: 0,
                type: 'event',
                event: 'stopped',
                body: {
                    reason: 'step',
                    threadId: 1,
                    allThreadsStopped: true
                }
            });
        }, 50);
    }
    handleStepIn(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        setTimeout(() => {
            this.sendEvent({
                seq: 0,
                type: 'event',
                event: 'stopped',
                body: {
                    reason: 'step',
                    threadId: 1,
                    allThreadsStopped: true
                }
            });
        }, 50);
    }
    handleStepOut(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        setTimeout(() => {
            this.sendEvent({
                seq: 0,
                type: 'event',
                event: 'stopped',
                body: {
                    reason: 'step',
                    threadId: 1,
                    allThreadsStopped: true
                }
            });
        }, 50);
    }
    handlePause(request) {
        this.isRunning = false;
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        this.sendEvent({
            seq: 0,
            type: 'event',
            event: 'stopped',
            body: {
                reason: 'pause',
                threadId: 1,
                allThreadsStopped: true
            }
        });
    }
    handleDisconnect(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        setTimeout(() => {
            process.exit(0);
        }, 100);
    }
    handleTerminate(request) {
        this.sendResponse({
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: true
        });
        this.sendEvent({
            seq: 0,
            type: 'event',
            event: 'terminated'
        });
        setTimeout(() => {
            process.exit(0);
        }, 100);
    }
    sendResponse(response) {
        if (this.connection) {
            this.connection.sendResponse(response);
        }
    }
    sendEvent(event) {
        if (this.connection) {
            this.connection.sendEvent(event);
        }
    }
    sendErrorResponse(request, id, message) {
        const response = {
            seq: 0,
            type: 'response',
            request_seq: request.seq,
            command: request.command,
            success: false,
            message,
            body: {
                error: {
                    id,
                    format: message
                }
            }
        };
        this.sendResponse(response);
    }
    getOrCreateVariableReference(data) {
        const ref = this.nextVariableReference++;
        this.variableHandles.set(ref, data);
        return ref;
    }
}
// Start the mock debug adapter process
new MockDebugAdapterProcess();
//# sourceMappingURL=mock-adapter-process.js.map