"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = mavenBatchExecutor;
const devkit_1 = require("@nx/devkit");
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = require("path");
const readline_1 = require("readline");
/**
 * Get path to the batch runner JAR
 */
function getBatchRunnerJar() {
    const jarPath = (0, path_1.resolve)(__dirname, '../../../dist/batch-runner.jar');
    if (!(0, fs_1.existsSync)(jarPath)) {
        throw new Error(`Maven batch runner JAR not found at: ${jarPath}`);
    }
    return jarPath;
}
/**
 * Normalize Maven arguments
 */
function normalizeMavenArgs(args) {
    if (!args)
        return [];
    if (Array.isArray(args))
        return args;
    return args
        .trim()
        .split(' ')
        .filter((arg) => arg.length > 0);
}
function buildTaskData(options, projectName) {
    return {
        phase: options.phase,
        goals: Array.isArray(options.goals)
            ? options.goals
            : options.goals
                ? [options.goals]
                : [],
        args: normalizeMavenArgs(options.args),
        project: projectName,
    };
}
/**
 * Build tasks map from task graph and inputs
 */
function buildTasks(taskGraph, inputs, context) {
    const tasks = {};
    for (const taskId of Object.keys(taskGraph.tasks)) {
        const task = taskGraph.tasks[taskId];
        const options = inputs[taskId];
        const nxProjectName = task.target.project;
        const projectConfig = context.projectGraph?.nodes?.[nxProjectName]?.data;
        const mavenProject = projectConfig?.metadata?.mavenProject ||
            nxProjectName;
        tasks[taskId] = buildTaskData(options, mavenProject);
    }
    return tasks;
}
/**
 * Maven batch executor using Kotlin batch runner with Maven Invoker API.
 * Streams task results as they complete via async generator.
 */
async function* mavenBatchExecutor(taskGraph, inputs, overrides, context) {
    // Get batch runner JAR path
    const batchRunnerJar = getBatchRunnerJar();
    // Build task map for batch runner
    const tasks = buildTasks(taskGraph, inputs, context);
    // Build arguments for batch runner
    const args = [];
    if (overrides.__overrides_unparsed__?.length) {
        args.push(...overrides.__overrides_unparsed__);
    }
    // Prepare batch runner arguments
    const javaArgs = ['-jar', batchRunnerJar, `--workspaceRoot=${devkit_1.workspaceRoot}`];
    if (process.env.NX_VERBOSE_LOGGING === 'true') {
        javaArgs.push('--verbose');
        console.log(`[Maven Batch] Executing: java ${javaArgs.join(' ')}`);
    }
    // Create combined payload for stdin with taskGraph, tasks, and args
    const stdinPayload = {
        taskGraph,
        tasks,
        args,
    };
    // Spawn batch runner process
    // stdin: pipe (send task data), stdout: inherit (Maven output), stderr: pipe (results)
    const child = (0, child_process_1.spawn)('java', javaArgs, {
        cwd: devkit_1.workspaceRoot,
        env: process.env,
        stdio: ['pipe', 'inherit', 'pipe'],
        windowsHide: true,
    });
    // Send task data via stdin (ignore EPIPE if process exits early)
    child.stdin.on('error', (err) => {
        if (err.code !== 'EPIPE') {
            console.error('[Maven Batch] stdin error:', err);
        }
    });
    child.stdin.write(JSON.stringify(stdinPayload));
    child.stdin.end();
    // Read stderr line by line for JSON results
    const rl = (0, readline_1.createInterface)({
        input: child.stderr,
        crlfDelay: Infinity,
    });
    // Collect non-result stderr lines for error reporting
    const stderrLines = [];
    // Collect terminal output from failed tasks
    const failedTaskOutputs = [];
    // Yield results as they stream in
    for await (const line of rl) {
        if (line.startsWith('NX_RESULT:')) {
            try {
                const jsonStr = line.slice('NX_RESULT:'.length);
                const data = JSON.parse(jsonStr);
                const result = {
                    success: data.result.success ?? false,
                    terminalOutput: data.result.terminalOutput ?? '',
                    startTime: data.result.startTime,
                    endTime: data.result.endTime,
                };
                // Collect terminal output from failed tasks
                if (!result.success && result.terminalOutput) {
                    failedTaskOutputs.push(result.terminalOutput);
                }
                yield {
                    task: data.task,
                    result,
                };
            }
            catch (e) {
                console.error('[Maven Batch] Failed to parse result line:', line, e);
            }
        }
        else if (line.trim()) {
            // Collect non-empty stderr lines for error reporting
            stderrLines.push(line);
        }
    }
    // Wait for process to exit
    await new Promise((resolve, reject) => {
        child.on('close', (code) => {
            if (process.env.NX_VERBOSE_LOGGING === 'true') {
                console.log(`[Maven Batch] Process exited with code: ${code}`);
            }
            // If process exited unexpectedly, print captured stderr
            if (code !== 0 && stderrLines.length > 0) {
                console.error(`[Maven Batch] Process exited with code ${code}. Stderr output:`);
                for (const line of stderrLines) {
                    console.error(line);
                }
            }
            // Print failed task outputs to stderr so they appear in error.message
            if (failedTaskOutputs.length > 0) {
                console.error('\n[Maven Batch] Failed task outputs:');
                for (const output of failedTaskOutputs) {
                    console.error(output);
                }
            }
            // Reject promise if batch runner exited with non-zero code
            if (code !== 0) {
                reject(new Error(`Maven batch runner exited with code ${code}`));
            }
            else {
                resolve();
            }
        });
        child.on('error', reject);
    });
}
