"use strict";
/**
 * @fileoverview Test generator compilation and execution utilities.
 * Provides functions to run test generators and create test files.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runGenerator = runGenerator;
exports.ensureGeneratorsExist = ensureGeneratorsExist;
exports.compileGenerator = compileGenerator;
exports.compileAllGenerators = compileAllGenerators;
exports.compileGeneratorsForTestsets = compileGeneratorsForTestsets;
exports.executeGeneratorScript = executeGeneratorScript;
const executor_1 = require("../executor");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const utils_1 = require("./utils");
const utils_2 = require("./utils");
const formatter_1 = require("../formatter");
/**
 * Runs a generator program to create a test file.
 * Executes the compiled generator with given arguments and redirects output to a file.
 *
 * @param {string} execCommand - Path to compiled generator executable
 * @param {string[]} args - Arguments to pass to the generator
 * @param {string} outputFilePath - Path where test output will be written
 *
 * @throws {Error} If generator execution fails
 * @throws {Error} If generator exceeds time or memory limits (exits process)
 *
 * @example
 * await runGenerator(
 *   './gen-random',
 *   ['1', '100'],
 *   'tests/test1.txt'
 * );
 */
async function runGenerator(execCommand, args, outputFilePath) {
    const argsString = args.join(' ');
    await executor_1.executor.executeWithRedirect(`${execCommand} ${argsString}`, {
        timeout: utils_2.DEFAULT_TIMEOUT,
        memoryLimitMB: utils_2.DEFAULT_MEMORY_LIMIT,
        silent: true,
        onTimeout: () => {
            formatter_1.fmt.error(`${formatter_1.fmt.cross()} ${formatter_1.fmt.bold('Generator Unexpectedly Exceeded Time Limit!')} (${utils_2.DEFAULT_TIMEOUT}ms)`);
            executor_1.executor.cleanup();
            process.exit(1);
        },
        onMemoryExceeded: () => {
            formatter_1.fmt.error(`${formatter_1.fmt.cross()} ${formatter_1.fmt.bold('Generator Unexpectedly Exceeded Memory Limit!')} (${utils_2.DEFAULT_MEMORY_LIMIT} MB)`);
            executor_1.executor.cleanup();
            process.exit(1);
        },
    }, undefined, outputFilePath);
}
/**
 * Ensures generators are defined in configuration.
 * Type assertion function that throws if no generators exist.
 *
 * @param {LocalGenerator[] | undefined} generators - Generators array to validate
 *
 * @throws {Error} If no generators are defined in configuration
 *
 * @example
 * const config = readConfigFile();
 * ensureGeneratorsExist(config.generators);
 * // Now TypeScript knows generators is defined
 */
function ensureGeneratorsExist(generators) {
    if (!generators || generators.length === 0) {
        throw new Error('No test generators defined in the configuration file.');
    }
}
/**
 * Compiles a generator program.
 * Wrapper around compileCPP with consistent error handling.
 *
 * @param {LocalGenerator} generator - Generator configuration
 * @returns {Promise<string>} Path to compiled generator executable
 *
 * @throws {Error} If compilation fails
 *
 * @example
 * const path = await compileGenerator({ name: 'gen', source: 'generators/gen.cpp' });
 */
async function compileGenerator(generator) {
    try {
        if (!generator.source) {
            throw new Error(`Generator ${generator.name} has no source file specified`);
        }
        const compiledPath = await (0, utils_1.compileCPP)(generator.source);
        return compiledPath;
    }
    catch (error) {
        throw error instanceof Error
            ? error
            : new Error(`Failed to compile generator ${generator.name}`);
    }
}
/**
 * Compiles all generators needed for the given commands.
 * Returns a map of generator names to compiled paths.
 *
 * @param {GeneratorScriptCommand[]} commands - Generator commands to analyze
 * @param {LocalGenerator[]} generators - Available generators
 * @returns {Promise<Map<string, string>>} Map of generator name to compiled path
 *
 * @throws {Error} If any generator compilation fails
 * @throws {Error} If a required generator is not found
 *
 * @example
 * const compiledPaths = await compileAllGenerators(commands, generators);
 * const genPath = compiledPaths.get('gen-random');
 */
async function compileAllGenerators(commands, generators) {
    const compiledGenerators = new Map();
    for (const command of commands) {
        if ((command.type === 'generator-single' ||
            command.type === 'generator-range') &&
            command.generator) {
            if (!compiledGenerators.has(command.generator)) {
                const generator = generators.find(g => g.name === command.generator);
                if (!generator) {
                    throw new Error(`Generator "${command.generator}" not found in configuration`);
                }
                try {
                    await compileGenerator(generator);
                    compiledGenerators.set(command.generator, (0, utils_1.getCompiledCommandToRun)(generator));
                }
                catch (error) {
                    (0, utils_1.throwError)(error, `Failed to compile generator ${command.generator}`);
                }
            }
        }
    }
    return compiledGenerators;
}
/**
 * Compiles all unique generators used across all testsets.
 *
 * @param {LocalTestset[]} testsets - Testsets to analyze for generator usage
 * @param {LocalGenerator[]} generators - Available generators
 * @returns {Promise<void>} Resolves when all generators are compiled
 *
 * @throws {Error} If any generator compilation fails
 *
 * @example
 * await compileGeneratorsForTestsets(config.testsets, config.generators);
 */
async function compileGeneratorsForTestsets(testsets, generators) {
    const uniqueGeneratorNames = new Set();
    // Collect all unique generator names from all testsets
    for (const testset of testsets) {
        if (testset.generatorScript?.commands) {
            for (const command of testset.generatorScript.commands) {
                if ((command.type === 'generator-single' ||
                    command.type === 'generator-range') &&
                    command.generator) {
                    uniqueGeneratorNames.add(command.generator);
                }
            }
        }
    }
    // Compile each unique generator
    for (const generatorName of uniqueGeneratorNames) {
        const generator = generators.find(g => g.name === generatorName);
        if (!generator) {
            throw new Error(`Generator "${generatorName}" not found in configuration`);
        }
        await compileGenerator(generator);
    }
}
/**
 * Executes generation script commands to create test files.
 * Processes each command in the script, handling both manual and generated tests.
 * Requires generators to be pre-compiled (use compileAllGenerators first).
 *
 * @param {GeneratorScriptCommand[]} commands - Array of generation commands
 * @param {LocalGenerator[]} generators - Available generators
 * @param {string} [outputDir] - Output directory for tests (defaults to ./tests)
 * @param {number} [startIndex] - Starting test index (defaults to 1)
 *
 * @throws {Error} If any command fails to execute
 *
 * @example
 * await executeGeneratorScript(
 *   [
 *     { type: 'manual', manualFile: './tests/manual/sample.txt' },
 *     { type: 'generator-single', generator: 'gen-random', args: ['1'] }
 *   ],
 *   generators,
 *   './tests/my-testset'
 * );
 */
async function executeGeneratorScript(commands, generators, outputDir) {
    let someFailed = false;
    const testsDir = outputDir || path_1.default.resolve(process.cwd(), 'testsets');
    (0, utils_1.ensureDirectoryExists)(testsDir);
    // Get compiled paths for all generators
    const compiledGenerators = new Map();
    for (const generator of generators) {
        const compiledPath = (0, utils_1.getCompiledCommandToRun)(generator);
        compiledGenerators.set(generator.name, compiledPath);
    }
    let testNumber = 1;
    for (const command of commands) {
        try {
            if (command.type === 'manual' && command.manualFile) {
                // Copy manual test file
                const testFilePath = path_1.default.join(testsDir, `test${testNumber++}.txt`);
                await copyManualTest(command.manualFile, testFilePath);
            }
            else if (command.type === 'generator-single' && command.generator) {
                // Run generator once
                const testFilePath = path_1.default.join(testsDir, `test${testNumber++}.txt`);
                const compiledPath = compiledGenerators.get(command.generator);
                if (!compiledPath) {
                    throw new Error(`Generator "${command.generator}" not compiled`);
                }
                await runGenerator(compiledPath, [command.number.toString()], testFilePath);
            }
            else if (command.type === 'generator-range' && command.generator) {
                // Run generator multiple times for a range
                const compiledPath = compiledGenerators.get(command.generator);
                if (!compiledPath) {
                    throw new Error(`Generator "${command.generator}" not compiled`);
                }
                if (!command.range || command.range.length !== 2) {
                    throw new Error(`Generator range command for "${command.generator}" missing valid range [start, end]`);
                }
                const [start, end] = command.range;
                for (let i = start; i <= end; i++) {
                    const testFilePath = path_1.default.join(testsDir, `test${testNumber++}.txt`);
                    const args = [i.toString()];
                    await runGenerator(compiledPath, args, testFilePath);
                }
            }
            else {
                throw new Error(`Invalid command type or missing required fields`);
            }
        }
        catch (error) {
            someFailed = true;
            formatter_1.fmt.error(`  ${formatter_1.fmt.cross()} Test ${command.index} generation failed:\n\t${error.message}`);
        }
    }
    if (someFailed) {
        throw new Error('Some tests failed to generate');
    }
}
/**
 * Copies a manual test file to the tests directory.
 *
 * @private
 * @param {string} sourceFilePath - Path to manual test file
 * @param {string} destFilePath - Destination path in tests directory
 *
 * @throws {Error} If file doesn't exist or copy fails
 *
 * @example
 * await copyManualTest('./tests/manual/sample1.txt', 'tests/test1.txt');
 */
async function copyManualTest(sourceFilePath, destFilePath) {
    return new Promise((resolve, reject) => {
        const sourcePath = path_1.default.resolve(process.cwd(), sourceFilePath);
        if (!fs_1.default.existsSync(sourcePath)) {
            return reject(new Error(`Manual test file not found: ${sourceFilePath}`));
        }
        fs_1.default.copyFile(sourcePath, destFilePath, err => {
            if (err) {
                return reject(new Error(`Failed to copy manual test: ${err.message}`));
            }
            resolve();
        });
    });
}
//# sourceMappingURL=generator.js.map