/**
 * Python Debug Adapter implementation
 *
 * Provides Python-specific debugging functionality using debugpy.
 * Encapsulates all Python-specific logic including executable discovery,
 * environment validation, and debugpy integration.
 *
 * @since 2.0.0
 */
import { EventEmitter } from 'events';
import { spawn } from 'child_process';
import * as path from 'path';
import { AdapterState, DebugFeature, AdapterError, AdapterErrorCode } from '../debug-adapter-interface.js';
import { DebugLanguage } from '../../session/models.js';
import { findPythonExecutable, getPythonVersion } from '../../utils/python-utils.js';
/**
 * Python Debug Adapter implementation
 */
export class PythonDebugAdapter extends EventEmitter {
    language = DebugLanguage.PYTHON;
    name = 'Python Debug Adapter';
    state = AdapterState.UNINITIALIZED;
    dependencies;
    // Caching
    pythonPathCache = new Map();
    cacheTimeout = 60000; // 1 minute
    // State
    currentThreadId = null;
    connected = false;
    constructor(dependencies) {
        super();
        this.dependencies = dependencies;
    }
    // ===== 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 || 'Python environment validation failed', AdapterErrorCode.ENVIRONMENT_INVALID);
            }
            this.transitionTo(AdapterState.READY);
            this.emit('initialized');
        }
        catch (error) {
            this.transitionTo(AdapterState.ERROR);
            throw error;
        }
    }
    async dispose() {
        this.pythonPathCache.clear();
        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;
        this.state = newState;
        this.emit('stateChanged', oldState, newState);
    }
    // ===== Environment Validation =====
    async validateEnvironment() {
        const errors = [];
        const warnings = [];
        try {
            // Check Python executable
            const pythonPath = await this.resolveExecutablePath();
            // Check Python version
            const version = await this.checkPythonVersion(pythonPath);
            if (version) {
                const [major, minor] = version.split('.').map(Number);
                if (major < 3 || (major === 3 && minor < 7)) {
                    errors.push({
                        code: 'PYTHON_VERSION_TOO_OLD',
                        message: `Python 3.7 or higher required. Current version: ${version}`,
                        recoverable: false
                    });
                }
            }
            else {
                warnings.push({
                    code: 'PYTHON_VERSION_CHECK_FAILED',
                    message: 'Could not determine Python version'
                });
            }
            // Check debugpy installation
            const hasDebugpy = await this.checkDebugpyInstalled(pythonPath);
            if (!hasDebugpy) {
                errors.push({
                    code: 'DEBUGPY_NOT_INSTALLED',
                    message: 'debugpy not installed. Run: pip install debugpy',
                    recoverable: true
                });
            }
            // Check if in virtual environment
            const isVenv = await this.detectVirtualEnv(pythonPath);
            if (isVenv) {
                this.dependencies.logger?.info('[PythonDebugAdapter] Virtual environment detected');
            }
        }
        catch (error) {
            errors.push({
                code: 'PYTHON_NOT_FOUND',
                message: error instanceof Error ? error.message : 'Python executable not found',
                recoverable: false
            });
        }
        return {
            valid: errors.length === 0,
            errors,
            warnings
        };
    }
    getRequiredDependencies() {
        return [
            {
                name: 'Python',
                version: '3.7+',
                required: true,
                installCommand: 'Download from https://python.org'
            },
            {
                name: 'debugpy',
                version: 'latest',
                required: true,
                installCommand: 'pip install debugpy'
            }
        ];
    }
    // ===== Executable Management =====
    async resolveExecutablePath(preferredPath) {
        // Check cache first
        const cacheKey = preferredPath || 'default';
        const cached = this.pythonPathCache.get(cacheKey);
        if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
            this.dependencies.logger?.debug(`[PythonDebugAdapter] Using cached Python path: ${cached.path}`);
            return cached.path;
        }
        // Find Python executable
        const pythonPath = await findPythonExecutable(preferredPath, this.dependencies.logger);
        // Cache the result
        this.pythonPathCache.set(cacheKey, {
            path: pythonPath,
            timestamp: Date.now()
        });
        return pythonPath;
    }
    getDefaultExecutableName() {
        switch (process.platform) {
            case 'win32':
                return 'py';
            default:
                return 'python3';
        }
    }
    getExecutableSearchPaths() {
        const paths = [];
        // Add common Python installation paths
        if (process.platform === 'win32') {
            paths.push('C:\\Python39', 'C:\\Python38', 'C:\\Python37', 'C:\\Program Files\\Python39', 'C:\\Program Files\\Python38', 'C:\\Program Files\\Python37', `${process.env.LOCALAPPDATA}\\Programs\\Python\\Python39`, `${process.env.LOCALAPPDATA}\\Programs\\Python\\Python38`, `${process.env.LOCALAPPDATA}\\Programs\\Python\\Python37`);
        }
        else if (process.platform === 'darwin') {
            paths.push('/usr/local/bin', '/opt/homebrew/bin', '/usr/bin');
        }
        else {
            paths.push('/usr/bin', '/usr/local/bin', '/opt/python/bin');
        }
        // Add PATH directories
        if (process.env.PATH) {
            paths.push(...process.env.PATH.split(path.delimiter));
        }
        return paths;
    }
    // ===== Adapter Configuration =====
    buildAdapterCommand(config) {
        return {
            command: config.executablePath,
            args: [
                '-m', 'debugpy.adapter',
                '--host', config.adapterHost,
                '--port', config.adapterPort.toString()
            ],
            env: {
                ...process.env,
                PYTHONUNBUFFERED: '1', // Ensure unbuffered output
                DEBUGPY_LOG_DIR: config.logDir
            }
        };
    }
    getAdapterModuleName() {
        return 'debugpy.adapter';
    }
    getAdapterInstallCommand() {
        return 'pip install debugpy';
    }
    // ===== Debug Configuration =====
    transformLaunchConfig(config) {
        const pythonConfig = {
            ...config,
            type: 'python',
            request: 'launch',
            name: 'Python: Current File',
            console: 'integratedTerminal',
            redirectOutput: true,
            showReturnValue: true,
            justMyCode: config.justMyCode ?? true,
            stopOnEntry: config.stopOnEntry ?? false
        };
        return pythonConfig;
    }
    getDefaultLaunchConfig() {
        return {
            stopOnEntry: false,
            justMyCode: true,
            env: {},
            cwd: process.cwd()
        };
    }
    // ===== DAP Protocol Operations =====
    async sendDapRequest(command, args) {
        // This will be handled by ProxyManager
        // Adapter just needs to validate the request is appropriate for Python
        // Validate Python-specific commands
        if (command === 'setExceptionBreakpoints' && args) {
            const exceptionArgs = args;
            // Ensure Python exception filters are valid
            const validFilters = ['raised', 'uncaught', 'userUnhandled'];
            const invalidFilters = exceptionArgs.filters?.filter(f => !validFilters.includes(f));
            if (invalidFilters?.length) {
                throw new AdapterError(`Invalid Python exception filters: ${invalidFilters.join(', ')}`, AdapterErrorCode.INVALID_RESPONSE);
            }
        }
        // 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.emit(event.event, event.body);
    }
    handleDapResponse(_response) {
        // Python adapter doesn't need special response handling
        void _response;
    }
    // ===== Connection Management =====
    async connect(host, port) {
        // Connection is handled by ProxyManager
        // Store connection info for debugging purposes
        this.dependencies.logger?.debug(`[PythonDebugAdapter] 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 `Python Debugging Setup:

1. Install Python 3.7 or higher:
   - Windows: Download from https://python.org
   - macOS: brew install python3
   - Linux: sudo apt install python3 python3-pip

2. Install debugpy:
   pip install debugpy

3. Verify installation:
   python -m debugpy --version

For virtual environments:
   python -m venv myenv
   source myenv/bin/activate  # On Windows: myenv\\Scripts\\activate
   pip install debugpy`;
    }
    getMissingExecutableError() {
        return `Python not found. Please ensure Python 3.7+ is installed and available in PATH.
    
Windows users: Try 'py' command or install from https://python.org
macOS users: Try 'brew install python3'
Linux users: Try 'sudo apt install python3'

You can also specify the Python path explicitly in your debug configuration.`;
    }
    translateErrorMessage(error) {
        const message = error.message.toLowerCase();
        if (message.includes('debugpy') && message.includes('modulenotfounderror')) {
            return 'debugpy is not installed. Please run: pip install debugpy';
        }
        if (message.includes('python') && message.includes('not found')) {
            return this.getMissingExecutableError();
        }
        if (message.includes('permission denied')) {
            return `Permission denied accessing Python executable. Check file permissions.`;
        }
        if (message.includes('windows store')) {
            return `Windows Store Python alias detected. Please install Python from https://python.org`;
        }
        return error.message;
    }
    // ===== Feature Support =====
    supportsFeature(feature) {
        const supportedFeatures = [
            DebugFeature.CONDITIONAL_BREAKPOINTS,
            DebugFeature.FUNCTION_BREAKPOINTS,
            DebugFeature.EXCEPTION_BREAKPOINTS,
            DebugFeature.VARIABLE_PAGING,
            DebugFeature.EVALUATE_FOR_HOVERS,
            DebugFeature.SET_VARIABLE,
            DebugFeature.LOG_POINTS,
            DebugFeature.TERMINATE_REQUEST,
            DebugFeature.EXCEPTION_OPTIONS,
            DebugFeature.EXCEPTION_INFO_REQUEST
        ];
        return supportedFeatures.includes(feature);
    }
    getFeatureRequirements(feature) {
        const requirements = [];
        switch (feature) {
            case DebugFeature.CONDITIONAL_BREAKPOINTS:
                requirements.push({
                    type: 'dependency',
                    description: 'debugpy 1.0+',
                    required: true
                });
                break;
            case DebugFeature.LOG_POINTS:
                requirements.push({
                    type: 'version',
                    description: 'debugpy 1.5+',
                    required: true
                });
                break;
            case DebugFeature.EXCEPTION_INFO_REQUEST:
                requirements.push({
                    type: 'version',
                    description: 'Python 3.7+',
                    required: true
                });
                break;
        }
        return requirements;
    }
    getCapabilities() {
        return {
            supportsConfigurationDoneRequest: true,
            supportsFunctionBreakpoints: true,
            supportsConditionalBreakpoints: true,
            supportsHitConditionalBreakpoints: true,
            supportsEvaluateForHovers: true,
            exceptionBreakpointFilters: [
                {
                    filter: 'raised',
                    label: 'Raised Exceptions',
                    description: 'Break on all raised exceptions',
                    default: false,
                    supportsCondition: true
                },
                {
                    filter: 'uncaught',
                    label: 'Uncaught Exceptions',
                    description: 'Break on uncaught exceptions',
                    default: true,
                    supportsCondition: true
                },
                {
                    filter: 'userUnhandled',
                    label: 'User Unhandled Exceptions',
                    description: 'Break on exceptions not handled by user code',
                    default: false,
                    supportsCondition: true
                }
            ],
            supportsStepBack: false,
            supportsSetVariable: true,
            supportsRestartFrame: false,
            supportsGotoTargetsRequest: false,
            supportsStepInTargetsRequest: true,
            supportsCompletionsRequest: true,
            completionTriggerCharacters: ['.', '['],
            supportsModulesRequest: true,
            supportsRestartRequest: false,
            supportsExceptionOptions: true,
            supportsValueFormattingOptions: true,
            supportsExceptionInfoRequest: true,
            supportTerminateDebuggee: true,
            supportSuspendDebuggee: false,
            supportsDelayedStackTraceLoading: true,
            supportsLoadedSourcesRequest: true,
            supportsLogPoints: true,
            supportsTerminateThreadsRequest: false,
            supportsSetExpression: false,
            supportsTerminateRequest: true,
            supportsDataBreakpoints: false,
            supportsReadMemoryRequest: false,
            supportsWriteMemoryRequest: false,
            supportsDisassembleRequest: false,
            supportsCancelRequest: false,
            supportsBreakpointLocationsRequest: true,
            supportsClipboardContext: false,
            supportsSteppingGranularity: false,
            supportsInstructionBreakpoints: false,
            supportsExceptionFilterOptions: true,
            supportsSingleThreadExecutionRequests: false
        };
    }
    // ===== Python-specific helper methods =====
    /**
     * Check Python version
     */
    async checkPythonVersion(pythonPath) {
        // Check cache first
        const cached = this.pythonPathCache.get(pythonPath);
        if (cached?.version) {
            return cached.version;
        }
        const version = await getPythonVersion(pythonPath);
        // Update cache
        if (version && cached) {
            cached.version = version;
        }
        return version;
    }
    /**
     * Check if debugpy is installed
     */
    async checkDebugpyInstalled(pythonPath) {
        // Check cache first
        const cached = this.pythonPathCache.get(pythonPath);
        if (cached?.hasDebugpy !== undefined) {
            return cached.hasDebugpy;
        }
        return new Promise((resolve) => {
            const child = spawn(pythonPath, ['-c', 'import debugpy; print(debugpy.__version__)'], {
                stdio: ['ignore', 'pipe', 'pipe']
            });
            let output = '';
            child.stdout?.on('data', (data) => { output += data.toString(); });
            child.on('error', () => resolve(false));
            child.on('exit', (code) => {
                const hasDebugpy = code === 0 && output.trim().length > 0;
                // Update cache
                if (cached) {
                    cached.hasDebugpy = hasDebugpy;
                }
                if (hasDebugpy) {
                    this.dependencies.logger?.info(`[PythonDebugAdapter] debugpy version: ${output.trim()}`);
                }
                resolve(hasDebugpy);
            });
        });
    }
    /**
     * Detect if Python is in a virtual environment
     */
    async detectVirtualEnv(pythonPath) {
        return new Promise((resolve) => {
            const child = spawn(pythonPath, ['-c', 'import sys; print(hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix))'], {
                stdio: ['ignore', 'pipe', 'ignore']
            });
            let output = '';
            child.stdout?.on('data', (data) => { output += data.toString(); });
            child.on('error', () => resolve(false));
            child.on('exit', () => {
                resolve(output.trim().toLowerCase() === 'true');
            });
        });
    }
}
//# sourceMappingURL=python-debug-adapter.js.map