/**
 * DAP connection management utilities
 */
export class DapConnectionManager {
    dapClientFactory;
    logger;
    INITIAL_CONNECT_DELAY = 500;
    MAX_CONNECT_ATTEMPTS = 60;
    CONNECT_RETRY_INTERVAL = 200;
    constructor(dapClientFactory, logger) {
        this.dapClientFactory = dapClientFactory;
        this.logger = logger;
    }
    /**
     * Connect to DAP adapter with retry logic
     */
    async connectWithRetry(host, port) {
        this.logger.info(`[ConnectionManager] Waiting ${this.INITIAL_CONNECT_DELAY}ms before first DAP connect attempt.`);
        await new Promise(resolve => setTimeout(resolve, this.INITIAL_CONNECT_DELAY));
        const client = this.dapClientFactory.create(host, port);
        // Temporary error handler to prevent unhandled 'error' event crashes during connect attempts
        const tempErrorHandler = (err) => {
            this.logger.debug(`[ConnectionManager] DAP client emitted 'error' during connection phase (expected for retries): ${err.message}`);
        };
        client.on('error', tempErrorHandler);
        let connectAttempts = 0;
        while (connectAttempts < this.MAX_CONNECT_ATTEMPTS) {
            try {
                this.logger.info(`[ConnectionManager] Attempting DAP client connect (attempt ${connectAttempts + 1}/${this.MAX_CONNECT_ATTEMPTS}) to ${host}:${port}`);
                await client.connect();
                this.logger.info('[ConnectionManager] DAP client connected to adapter successfully.');
                // Remove temporary handler as connection succeeded
                client.off('error', tempErrorHandler);
                return client;
            }
            catch (err) {
                connectAttempts++;
                const errMessage = err instanceof Error ? err.message : String(err);
                if (connectAttempts >= this.MAX_CONNECT_ATTEMPTS) {
                    this.logger.error(`[ConnectionManager] Failed to connect DAP client after ${this.MAX_CONNECT_ATTEMPTS} attempts. Last error: ${errMessage}`);
                    client.off('error', tempErrorHandler);
                    throw new Error(`Failed to connect DAP client: ${errMessage}`);
                }
                this.logger.warn(`[ConnectionManager] DAP client connect attempt ${connectAttempts} failed: ${errMessage}. Retrying in ${this.CONNECT_RETRY_INTERVAL}ms...`);
                await new Promise(resolve => setTimeout(resolve, this.CONNECT_RETRY_INTERVAL));
            }
        }
        // This should never be reached due to the throw above, but TypeScript needs it
        throw new Error('Connection retry loop exited unexpectedly');
    }
    /**
     * Initialize DAP session
     */
    async initializeSession(client, sessionId) {
        const initializeArgs = {
            clientID: `mcp-proxy-${sessionId}`,
            clientName: 'MCP Debug Proxy',
            adapterID: 'python',
            pathFormat: 'path',
            linesStartAt1: true,
            columnsStartAt1: true,
            supportsVariableType: true,
            supportsRunInTerminalRequest: false,
            locale: 'en-US'
        };
        this.logger.info('[ConnectionManager] Sending DAP "initialize" request');
        await client.sendRequest('initialize', initializeArgs);
        this.logger.info('[ConnectionManager] DAP "initialize" request sent and response received.');
    }
    /**
     * Set up event handlers for a DAP client
     */
    setupEventHandlers(client, handlers) {
        if (handlers.onInitialized) {
            client.on('initialized', handlers.onInitialized);
        }
        if (handlers.onOutput) {
            client.on('output', handlers.onOutput);
        }
        if (handlers.onStopped) {
            client.on('stopped', handlers.onStopped);
        }
        if (handlers.onContinued) {
            client.on('continued', handlers.onContinued);
        }
        if (handlers.onThread) {
            client.on('thread', handlers.onThread);
        }
        if (handlers.onExited) {
            client.on('exited', handlers.onExited);
        }
        if (handlers.onTerminated) {
            client.on('terminated', handlers.onTerminated);
        }
        if (handlers.onError) {
            client.on('error', handlers.onError);
        }
        if (handlers.onClose) {
            client.on('close', handlers.onClose);
        }
        this.logger.info('[ConnectionManager] DAP event handlers set up');
    }
    /**
     * Disconnect DAP client gracefully
     */
    async disconnect(client, terminateDebuggee = true) {
        if (!client) {
            this.logger.info('[ConnectionManager] No active DAP client to disconnect.');
            return;
        }
        this.logger.info('[ConnectionManager] Attempting graceful DAP disconnect.');
        try {
            this.logger.info('[ConnectionManager] Sending "disconnect" request to DAP adapter...');
            await Promise.race([
                client.sendRequest('disconnect', { terminateDebuggee }),
                new Promise((_, reject) => setTimeout(() => reject(new Error('DAP disconnect request timed out after 1000ms')), 1000))
            ]);
            this.logger.info('[ConnectionManager] DAP "disconnect" request completed.');
        }
        catch (e) {
            const message = e instanceof Error ? e.message : String(e);
            this.logger.warn(`[ConnectionManager] Error or timeout during DAP "disconnect" request: ${message}`);
        }
        // Always call the client's disconnect method to clean up
        try {
            this.logger.info('[ConnectionManager] Calling client.disconnect() for final cleanup.');
            client.disconnect();
            this.logger.info('[ConnectionManager] Client disconnected.');
        }
        catch (e) {
            const message = e instanceof Error ? e.message : String(e);
            this.logger.error(`[ConnectionManager] Error calling client.disconnect(): ${message}`, e);
        }
    }
    /**
     * Send a launch request with proper configuration
     */
    async sendLaunchRequest(client, scriptPath, scriptArgs = [], stopOnEntry = true, justMyCode = true) {
        // DIAGNOSTIC: Log the incoming scriptPath
        this.logger.info('[ConnectionManager] DIAGNOSTIC: Received scriptPath:', scriptPath);
        this.logger.info('[ConnectionManager] DIAGNOSTIC: scriptPath type:', typeof scriptPath);
        this.logger.info('[ConnectionManager] DIAGNOSTIC: scriptPath length:', scriptPath.length);
        // Pass paths exactly as provided - no manipulation
        const launchArgs = {
            program: scriptPath,
            stopOnEntry,
            noDebug: false,
            args: scriptArgs,
            // Don't set cwd - let debugpy use its inherited working directory
            console: "internalConsole",
            justMyCode,
        };
        // DIAGNOSTIC: Log the launch args object before sending
        this.logger.info('[ConnectionManager] DIAGNOSTIC: launchArgs object:', JSON.stringify(launchArgs, null, 2));
        this.logger.info('[ConnectionManager] DIAGNOSTIC: launchArgs.program value:', launchArgs.program);
        this.logger.info('[ConnectionManager] Sending "launch" request to adapter with args:', launchArgs);
        await client.sendRequest('launch', launchArgs);
        this.logger.info('[ConnectionManager] DAP "launch" request sent.');
    }
    /**
     * Set breakpoints for a file
     */
    async setBreakpoints(client, sourcePath, breakpoints) {
        const sourceBreakpoints = breakpoints.map(bp => ({
            line: bp.line,
            condition: bp.condition
        }));
        const setBreakpointsArgs = {
            source: { path: sourcePath },
            breakpoints: sourceBreakpoints
        };
        this.logger.info(`[ConnectionManager] Setting ${breakpoints.length} breakpoint(s) for ${sourcePath}`);
        const response = await client.sendRequest('setBreakpoints', setBreakpointsArgs);
        this.logger.info('[ConnectionManager] Breakpoints set. Response:', response);
        return response;
    }
    /**
     * Send configuration done notification
     */
    async sendConfigurationDone(client) {
        this.logger.info('[ConnectionManager] Sending "configurationDone" to adapter.');
        await client.sendRequest('configurationDone', {});
        this.logger.info('[ConnectionManager] "configurationDone" sent.');
    }
}
//# sourceMappingURL=dap-proxy-connection-manager.js.map