/**
 * Plugin-based ATP Compiler
 *
 * Extensible compiler that supports custom plugins for detection,
 * transformation, optimization, and validation
 */
import { parse } from '@babel/parser';
import _traverse from '@babel/traverse';
const traverse = _traverse.default || _traverse;
import _generate from '@babel/generator';
const generate = _generate.default || _generate;
import { DEFAULT_COMPILER_CONFIG } from '../types.js';
import { TransformationError } from '../runtime/errors.js';
import { resetIdCounter } from '../runtime/context.js';
import { PluginRegistry } from './plugin-api.js';
/**
 * Plugin-based ATP Compiler
 *
 * @example
 * ```typescript
 * const compiler = new PluggableCompiler({
 *   enableBatchParallel: true
 * });
 *
 * // Register custom plugin
 * compiler.use(myCustomPlugin);
 *
 * // Transform code
 * const result = compiler.transform(code);
 * ```
 */
export class PluggableCompiler {
    config;
    registry;
    initialized = false;
    /**
     * AST cache - maps code string to parsed AST
     * This avoids re-parsing the same code multiple times
     * (e.g., once in detect() and once in transform())
     *
     * Performance Impact: ~30% reduction in compile time
     */
    astCache = new Map();
    constructor(config = {}) {
        this.config = { ...DEFAULT_COMPILER_CONFIG, ...config };
        this.registry = new PluginRegistry();
    }
    /**
     * Register a plugin (chainable)
     */
    use(plugin) {
        this.registry.register(plugin);
        return this;
    }
    /**
     * Unregister a plugin by name
     */
    remove(pluginName) {
        return this.registry.unregister(pluginName);
    }
    /**
     * Initialize all plugins
     */
    async initialize() {
        if (this.initialized)
            return;
        await this.registry.initializeAll(this.config);
        this.initialized = true;
    }
    /**
     * Detect patterns using all detection plugins
     */
    async detect(code) {
        if (!this.initialized) {
            await this.initialize();
        }
        try {
            const ast = this.parseCode(code);
            const detectors = this.registry.getDetectors();
            // Aggregate results from all detectors
            const allPatterns = new Set();
            let batchableParallel = false;
            for (const detector of detectors) {
                const result = await detector.detect(code, ast);
                result.patterns.forEach((p) => allPatterns.add(p));
                if (result.batchableParallel) {
                    batchableParallel = true;
                }
            }
            return {
                needsTransform: allPatterns.size > 0,
                patterns: Array.from(allPatterns),
                batchableParallel,
            };
        }
        catch (error) {
            return {
                needsTransform: false,
                patterns: [],
                batchableParallel: false,
            };
        }
    }
    /**
     * Transform code using all transformation plugins
     */
    async transform(code) {
        if (!this.initialized) {
            await this.initialize();
        }
        resetIdCounter();
        // 1. Pre-validation
        await this.runValidation(code, 'pre');
        // 2. Detection
        const detection = await this.detect(code);
        if (!detection.needsTransform) {
            return {
                code,
                transformed: false,
                patterns: [],
                metadata: {
                    loopCount: 0,
                    arrayMethodCount: 0,
                    parallelCallCount: 0,
                    batchableCount: 0,
                    checkpointCount: 0,
                },
            };
        }
        try {
            // 3. Parse AST
            const ast = this.parseCode(code);
            // 4. Reset all transformers
            const transformers = this.registry.getTransformers();
            for (const transformer of transformers) {
                transformer.reset();
            }
            // 5. Apply all transformation visitors
            // Combine visitors from all plugins, merging handlers for the same node type
            const visitors = {};
            for (const transformer of transformers) {
                const visitor = transformer.getVisitor(this.config);
                // Merge visitors, combining multiple handlers for the same node type
                for (const [nodeType, handler] of Object.entries(visitor)) {
                    if (!visitors[nodeType]) {
                        visitors[nodeType] = handler;
                    }
                    else {
                        // Combine multiple handlers for the same node type
                        const existing = visitors[nodeType];
                        visitors[nodeType] = (path) => {
                            existing(path);
                            handler(path);
                        };
                    }
                }
            }
            traverse(ast, visitors);
            // 6. Optimization
            let optimizedAst = ast;
            const optimizers = this.registry.getOptimizers();
            for (const optimizer of optimizers) {
                optimizedAst = await optimizer.optimize(optimizedAst, this.config);
            }
            // 7. Generate code
            const output = generate(optimizedAst, {
                sourceMaps: false,
                retainLines: true,
                comments: true,
            });
            // 8. Aggregate metadata
            const metadata = {
                loopCount: 0,
                arrayMethodCount: 0,
                parallelCallCount: 0,
                batchableCount: detection.batchableParallel ? 1 : 0,
                checkpointCount: 0,
            };
            for (const transformer of transformers) {
                const pluginMetadata = transformer.getMetadata();
                metadata.loopCount += pluginMetadata.loopCount || 0;
                metadata.arrayMethodCount += pluginMetadata.arrayMethodCount || 0;
                metadata.parallelCallCount += pluginMetadata.parallelCallCount || 0;
                metadata.batchableCount += pluginMetadata.batchableCount || 0;
            }
            // 9. Post-validation
            await this.runValidation(output.code, 'post');
            return {
                code: output.code,
                transformed: true,
                patterns: detection.patterns,
                metadata,
            };
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            // Include context about which phase failed
            const context = detection.patterns.length > 0 ? `[Plugin transformation] ${message}` : message;
            throw new TransformationError(context, code, 'plugin-error');
        }
    }
    /**
     * Dispose compiler and all plugins
     */
    async dispose() {
        await this.registry.disposeAll();
        this.astCache.clear(); // Clear cache on disposal
        this.initialized = false;
    }
    /**
     * Get current configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Update configuration
     */
    setConfig(config) {
        this.config = { ...this.config, ...config };
    }
    /**
     * Parse code to AST with caching
     *
     * Caches parsed AST to avoid re-parsing the same code multiple times.
     * This provides ~30% performance improvement when detect() and transform()
     * are called on the same code.
     */
    parseCode(code) {
        // Check cache first
        const cached = this.astCache.get(code);
        if (cached) {
            return cached;
        }
        // Parse and cache
        const ast = parse(code, {
            sourceType: 'module',
            plugins: ['typescript'],
            allowAwaitOutsideFunction: true,
            allowReturnOutsideFunction: true,
        });
        this.astCache.set(code, ast);
        return ast;
    }
    /**
     * Clear AST cache
     *
     * Call this method to free memory if you've compiled many different code snippets.
     * The cache is automatically managed and uses Map, so old entries don't leak memory.
     */
    clearCache() {
        this.astCache.clear();
    }
    /**
     * Get the compiler type identifier (ICompiler interface requirement)
     */
    getType() {
        return 'PluggableCompiler';
    }
    /**
     * Get cache statistics (for debugging/monitoring)
     */
    getCacheStats() {
        return {
            size: this.astCache.size,
            enabled: true,
        };
    }
    /**
     * Run validators
     */
    async runValidation(code, phase) {
        const validators = this.registry.getValidators();
        const ast = this.parseCode(code);
        for (const validator of validators) {
            await validator.validate(code, ast, phase);
        }
    }
}
//# sourceMappingURL=pluggable-compiler.js.map