"use strict";
/**
 * Parallel Execution System for Generators
 * Executes independent generators in parallel for significant performance improvements
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutionMetrics = exports.ParallelExecutor = void 0;
const os = __importStar(require("os"));
class ParallelExecutor {
    defaultMaxConcurrency;
    constructor() {
        // Calculate default concurrency: CPU cores - 1, min 2, max 8
        const cpuCount = os.cpus().length;
        this.defaultMaxConcurrency = Math.max(2, Math.min(8, cpuCount - 1));
    }
    /**
     * Execute generators in parallel with intelligent batching
     */
    async execute(generators, context, options = {}) {
        const startTime = Date.now();
        const maxConcurrency = options.maxConcurrency || this.defaultMaxConcurrency;
        const timeout = options.timeout || 60000;
        const results = new Map();
        const executionTimes = new Map();
        const allFiles = [];
        const allErrors = [];
        const allWarnings = [];
        // Create execution queue
        const queue = [...generators];
        let completed = 0;
        const total = generators.length;
        // Execute in batches
        while (queue.length > 0) {
            // Take up to maxConcurrency generators from queue
            const batch = queue.splice(0, maxConcurrency);
            // Execute batch in parallel
            const batchResults = await Promise.allSettled(batch.map(({ name, generator }) => this.executeGenerator(name, generator, context, timeout)));
            // Process results
            for (let i = 0; i < batch.length; i++) {
                const { name } = batch[i];
                const result = batchResults[i];
                completed++;
                if (result.status === 'fulfilled') {
                    const { result: genResult, duration } = result.value;
                    results.set(name, genResult);
                    executionTimes.set(name, duration);
                    // Collect files, errors, warnings
                    allFiles.push(...genResult.files);
                    if (genResult.errors) {
                        allErrors.push(...genResult.errors.map(e => `[${name}] ${e}`));
                    }
                    if (genResult.warnings) {
                        allWarnings.push(...genResult.warnings.map(w => `[${name}] ${w}`));
                    }
                    // Call progress callback
                    if (options.onProgress) {
                        options.onProgress(completed, total, name);
                    }
                }
                else {
                    // Generator failed
                    const error = result.reason;
                    const errorMessage = error instanceof Error ? error.message : String(error);
                    results.set(name, {
                        files: [],
                        errors: [errorMessage],
                    });
                    allErrors.push(`[${name}] ${errorMessage}`);
                    executionTimes.set(name, 0);
                    // Call progress callback
                    if (options.onProgress) {
                        options.onProgress(completed, total, name);
                    }
                    // Fail fast if enabled
                    if (options.failFast) {
                        throw new Error(`Generator "${name}" failed: ${errorMessage}`);
                    }
                }
            }
        }
        const totalDuration = Date.now() - startTime;
        return {
            results,
            allFiles,
            allErrors,
            allWarnings,
            executionTimes,
            totalDuration,
        };
    }
    /**
     * Execute a single generator with timeout
     */
    async executeGenerator(name, generator, context, timeout) {
        const startTime = Date.now();
        // Create timeout promise with clearable timer
        let timeoutId;
        const timeoutPromise = new Promise((_, reject) => {
            timeoutId = setTimeout(() => {
                reject(new Error(`Generator "${name}" timed out after ${timeout}ms`));
            }, timeout);
        });
        try {
            // Race between generator execution and timeout
            const result = await Promise.race([
                Promise.resolve(generator.generate(context)),
                timeoutPromise,
            ]);
            const duration = Date.now() - startTime;
            return { result, duration };
        }
        finally {
            // Always clear the timeout to prevent hanging handles
            clearTimeout(timeoutId);
        }
    }
    /**
     * Get recommended concurrency based on system resources and workload
     */
    getRecommendedConcurrency(generatorCount) {
        // For small workloads, don't over-parallelize
        if (generatorCount <= 2) {
            return generatorCount;
        }
        // For medium workloads, use default
        if (generatorCount <= this.defaultMaxConcurrency) {
            return this.defaultMaxConcurrency;
        }
        // For large workloads, use max concurrency
        return Math.max(this.defaultMaxConcurrency, Math.min(8, generatorCount));
    }
    /**
     * Analyze generator dependencies (for future dependency-aware execution)
     * Currently returns empty dependencies (all generators are independent)
     */
    analyzeDependencies(generators) {
        const dependencies = new Map();
        // Initialize with no dependencies
        for (const { name } of generators) {
            dependencies.set(name, []);
        }
        // Future enhancement: Analyze actual dependencies
        // e.g., backend generator might depend on database generator
        // For now, treat all generators as independent
        return dependencies;
    }
    /**
     * Create execution plan considering dependencies (for future use)
     * Currently returns simple batches since no dependencies exist
     */
    createExecutionPlan(generators, dependencies, maxConcurrency) {
        const plan = [];
        // For now, since no dependencies exist, just batch by concurrency
        const generatorNames = generators.map(g => g.name);
        for (let i = 0; i < generatorNames.length; i += maxConcurrency) {
            plan.push(generatorNames.slice(i, i + maxConcurrency));
        }
        return plan;
    }
}
exports.ParallelExecutor = ParallelExecutor;
/**
 * Utility class for tracking parallel execution metrics
 */
class ExecutionMetrics {
    metrics = new Map();
    /**
     * Record generator execution
     */
    record(name, duration, filesGenerated, success) {
        const endTime = Date.now();
        const startTime = endTime - duration;
        this.metrics.set(name, {
            duration,
            filesGenerated,
            success,
            startTime,
            endTime,
        });
    }
    /**
     * Get total execution time
     */
    getTotalTime() {
        if (this.metrics.size === 0)
            return 0;
        const times = Array.from(this.metrics.values());
        const earliest = Math.min(...times.map(t => t.startTime));
        const latest = Math.max(...times.map(t => t.endTime));
        return latest - earliest;
    }
    /**
     * Get average execution time per generator
     */
    getAverageTime() {
        if (this.metrics.size === 0)
            return 0;
        const totalDuration = Array.from(this.metrics.values())
            .reduce((sum, m) => sum + m.duration, 0);
        return totalDuration / this.metrics.size;
    }
    /**
     * Get slowest generator
     */
    getSlowestGenerator() {
        if (this.metrics.size === 0)
            return null;
        let slowest = null;
        for (const [name, metrics] of this.metrics.entries()) {
            if (!slowest || metrics.duration > slowest.duration) {
                slowest = { name, duration: metrics.duration };
            }
        }
        return slowest;
    }
    /**
     * Get fastest generator
     */
    getFastestGenerator() {
        if (this.metrics.size === 0)
            return null;
        let fastest = null;
        for (const [name, metrics] of this.metrics.entries()) {
            if (!fastest || metrics.duration < fastest.duration) {
                fastest = { name, duration: metrics.duration };
            }
        }
        return fastest;
    }
    /**
     * Get success rate
     */
    getSuccessRate() {
        if (this.metrics.size === 0)
            return 0;
        const successful = Array.from(this.metrics.values())
            .filter(m => m.success).length;
        return (successful / this.metrics.size) * 100;
    }
    /**
     * Get formatted summary
     */
    getSummary() {
        if (this.metrics.size === 0) {
            return 'No metrics recorded';
        }
        const lines = [];
        const totalTime = this.getTotalTime();
        const avgTime = this.getAverageTime();
        const slowest = this.getSlowestGenerator();
        const fastest = this.getFastestGenerator();
        const successRate = this.getSuccessRate();
        lines.push(`Parallel Execution Summary:`);
        lines.push(`  Total generators: ${this.metrics.size}`);
        lines.push(`  Wall clock time: ${totalTime}ms`);
        lines.push(`  Average time per generator: ${Math.round(avgTime)}ms`);
        lines.push(`  Success rate: ${successRate.toFixed(1)}%`);
        if (slowest) {
            lines.push(`  Slowest: ${slowest.name} (${slowest.duration}ms)`);
        }
        if (fastest) {
            lines.push(`  Fastest: ${fastest.name} (${fastest.duration}ms)`);
        }
        // Calculate speedup if we have enough data
        const totalSequentialTime = Array.from(this.metrics.values())
            .reduce((sum, m) => sum + m.duration, 0);
        if (totalTime > 0 && totalSequentialTime > totalTime) {
            const speedup = totalSequentialTime / totalTime;
            lines.push(`  Speedup: ${speedup.toFixed(2)}x`);
        }
        return lines.join('\n');
    }
    /**
     * Get all metrics
     */
    getAll() {
        return new Map(this.metrics);
    }
}
exports.ExecutionMetrics = ExecutionMetrics;
//# sourceMappingURL=parallel-executor.js.map