/**
 * Hook Executor
 *
 * Executes hooks in isolated environment and validates results.
 * Detects import errors, runtime errors, timeouts, and performance issues.
 *
 * Part of increment 0037: Hook Health Check System
 */
import { spawn } from 'child_process';
import * as path from 'path';
// CRITICAL: Import getCleanEnv to remove NODE_OPTIONS debugger flags from child processes
import { getCleanEnv } from '../../utils/clean-env.js';
/**
 * Hook Executor - Validates hook execution
 */
export class HookExecutor {
    constructor(config) {
        this.config = config;
    }
    /**
     * Execute hook and capture result
     */
    async executeHook(hook) {
        const startTime = Date.now();
        const timestamp = new Date().toISOString();
        try {
            const { exitCode, stdout, stderr } = await this.runHookProcess(hook);
            const executionTime = Date.now() - startTime;
            // Analyze execution result
            const errors = this.detectErrors(stderr, hook);
            const warnings = this.detectWarnings(stdout, stderr, executionTime, hook);
            const performanceIssue = this.checkPerformance(executionTime, hook);
            const success = exitCode === 0 && errors.length === 0;
            return {
                hook: hook.name,
                plugin: hook.plugin,
                success,
                exitCode,
                stdout,
                stderr,
                executionTime,
                errors,
                warnings,
                timestamp,
                performanceIssue,
                performanceRatio: hook.expectedDuration
                    ? executionTime / hook.expectedDuration
                    : undefined
            };
        }
        catch (error) {
            // Execution failed completely
            const executionTime = Date.now() - startTime;
            return {
                hook: hook.name,
                plugin: hook.plugin,
                success: false,
                exitCode: -1,
                stdout: '',
                stderr: error instanceof Error ? error.message : String(error),
                executionTime,
                errors: [{
                        type: 'runtime',
                        message: `Hook execution failed: ${error instanceof Error ? error.message : String(error)}`,
                        fixable: false
                    }],
                warnings: [],
                timestamp,
                performanceIssue: false
            };
        }
    }
    /**
     * Run hook as child process
     */
    async runHookProcess(hook) {
        return new Promise((resolve, reject) => {
            const args = this.getHookArgs(hook);
            const command = this.getHookCommand(hook);
            // CRITICAL: Use getCleanEnv() to remove NODE_OPTIONS debugger flags
            // Without this, running hooks from VSCode debug mode fails silently
            const childProcess = spawn(command, args, {
                cwd: path.dirname(hook.path),
                env: { ...getCleanEnv(), NODE_ENV: 'test' },
                timeout: this.config.timeout
            });
            let stdout = '';
            let stderr = '';
            if (this.config.captureStdout) {
                childProcess.stdout?.on('data', (data) => {
                    stdout += data.toString();
                });
            }
            if (this.config.captureStderr) {
                childProcess.stderr?.on('data', (data) => {
                    stderr += data.toString();
                });
            }
            childProcess.on('close', (exitCode) => {
                resolve({
                    exitCode: exitCode || 0,
                    stdout,
                    stderr
                });
            });
            childProcess.on('error', (error) => {
                reject(error);
            });
            // Handle timeout
            setTimeout(() => {
                if (!childProcess.killed) {
                    childProcess.kill('SIGTERM');
                    reject(new Error(`Hook execution timeout (${this.config.timeout}ms)`));
                }
            }, this.config.timeout);
        });
    }
    /**
     * Get command to execute hook
     */
    getHookCommand(hook) {
        return hook.type === 'shell' ? 'bash' : 'node';
    }
    /**
     * Get arguments for hook execution
     */
    getHookArgs(hook) {
        if (hook.type === 'shell') {
            return [hook.path];
        }
        else {
            // For TypeScript hooks, run the compiled JavaScript
            const jsPath = hook.path.replace(/\.ts$/, '.js').replace('/lib/hooks/', '/lib/hooks/');
            const distPath = jsPath.replace(/^plugins\//, 'dist/plugins/');
            return [distPath, this.config.testIncrementId];
        }
    }
    /**
     * Detect errors in hook execution
     */
    detectErrors(stderr, hook) {
        const errors = [];
        // Detect import errors (ERR_MODULE_NOT_FOUND)
        if (stderr.includes('ERR_MODULE_NOT_FOUND')) {
            const importError = this.parseImportError(stderr);
            if (importError) {
                errors.push({
                    type: 'import',
                    message: importError.message,
                    suggestion: importError.suggestion,
                    fixable: importError.fixable
                });
            }
        }
        // Detect static vs instance method errors
        if (stderr.includes('Did you mean to access the static member')) {
            errors.push({
                type: 'runtime',
                message: 'Using instance method instead of static method',
                suggestion: 'Use ClassName.methodName() instead of this.methodName()',
                fixable: false // Requires code review
            });
        }
        // Detect syntax errors
        if (stderr.includes('SyntaxError')) {
            const syntaxMatch = stderr.match(/SyntaxError: (.+)/);
            errors.push({
                type: 'syntax',
                message: syntaxMatch ? syntaxMatch[1] : 'Syntax error in hook code',
                fixable: false
            });
        }
        // Detect JSON parse errors
        if (stderr.includes('Unexpected token') && stderr.includes('JSON')) {
            errors.push({
                type: 'validation',
                message: 'Invalid JSON input to hook',
                suggestion: 'Validate hook input format matches expected schema',
                fixable: false
            });
        }
        return errors;
    }
    /**
     * Parse import error from stderr
     */
    parseImportError(stderr) {
        const moduleMatch = stderr.match(/Cannot find module '(.+?)'/);
        if (!moduleMatch) {
            return null;
        }
        const missingModule = moduleMatch[1];
        const hasExtension = missingModule.endsWith('.js');
        return {
            type: 'import',
            message: `Cannot find module '${missingModule}'`,
            suggestion: hasExtension
                ? `Verify module exists at path: ${missingModule}`
                : `Add .js extension: import from '${missingModule}.js'`,
            fixable: !hasExtension
        };
    }
    /**
     * Detect warnings in hook execution
     */
    detectWarnings(stdout, stderr, executionTime, hook) {
        const warnings = [];
        // Performance warning
        if (hook.expectedDuration && executionTime > hook.expectedDuration * 2) {
            warnings.push({
                type: 'performance',
                message: `Hook took ${executionTime}ms (expected ${hook.expectedDuration}ms)`,
                severity: 'medium',
                recommendation: 'Optimize hook performance or increase expected duration'
            });
        }
        // Deprecation warnings
        if (stderr.includes('DeprecationWarning')) {
            warnings.push({
                type: 'deprecation',
                message: 'Hook uses deprecated APIs',
                severity: 'low',
                recommendation: 'Update to use current APIs'
            });
        }
        return warnings;
    }
    /**
     * Check if hook has performance issues (>2x expected duration)
     */
    checkPerformance(executionTime, hook) {
        return hook.expectedDuration !== undefined && executionTime > hook.expectedDuration * 2;
    }
    /**
     * Extract import error details for auto-fixing
     */
    extractImportErrorDetails(stderr, hookPath) {
        const moduleMatch = stderr.match(/Cannot find module '(.+?)'/);
        const importingMatch = stderr.match(/imported from '(.+?)'/);
        if (!moduleMatch) {
            return null;
        }
        const modulePath = moduleMatch[1];
        const importingFile = importingMatch ? importingMatch[1] : hookPath;
        const hasExtension = modulePath.endsWith('.js');
        const isRelativeImport = !hasExtension && !modulePath.includes('node_modules');
        return {
            modulePath,
            importingFile,
            importStatement: '', // Would need to parse file to get exact statement
            hasExtension,
            suggestedPath: hasExtension ? modulePath : `${modulePath}.js`,
            fixConfidence: isRelativeImport ? 'high' : 'medium'
        };
    }
    /**
     * Create default executor config
     */
    static createDefaultConfig() {
        return {
            timeout: 5000, // 5 seconds
            captureStdout: true,
            captureStderr: true,
            testIncrementId: '__health-check-test__'
        };
    }
}
//# sourceMappingURL=HookExecutor.js.map