"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cancelPendingMavenAnalysis = cancelPendingMavenAnalysis;
exports.runMavenAnalysis = runMavenAnalysis;
const path_1 = require("path");
const fs_1 = require("fs");
const child_process_1 = require("child_process");
const devkit_1 = require("@nx/devkit");
const devkit_internals_1 = require("nx/src/devkit-internals");
const cache_directory_1 = require("nx/src/utils/cache-directory");
const detect_maven_executable_1 = require("../utils/detect-maven-executable");
const treeKill = require('tree-kill');
const DEFAULT_ANALYSIS_TIMEOUT_SECONDS = (0, devkit_internals_1.isCI)() ? 600 : 120;
let currentAbortController;
/**
 * Cancel any in-flight Maven analysis process.
 * Safe to call even if nothing is running.
 */
function cancelPendingMavenAnalysis() {
    if (currentAbortController) {
        currentAbortController.abort('cancelled');
        currentAbortController = undefined;
    }
}
function getAnalysisTimeoutMs() {
    const envTimeout = process.env.NX_MAVEN_ANALYSIS_TIMEOUT;
    if (envTimeout) {
        const parsed = Number(envTimeout);
        if (!Number.isNaN(parsed) && parsed > 0) {
            return parsed * 1000;
        }
    }
    return DEFAULT_ANALYSIS_TIMEOUT_SECONDS * 1000;
}
/**
 * Run Maven analysis using our Kotlin analyzer plugin
 */
async function runMavenAnalysis(workspaceRoot, options) {
    console.log(`[Maven Analyzer] Starting analysis with options:`, options);
    const outputFile = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'nx-maven-projects.json');
    const isVerbose = options.verbose || process.env.NX_VERBOSE_LOGGING === 'true';
    devkit_1.logger.verbose(`[Maven Analyzer] Output file: ${outputFile}`);
    devkit_1.logger.verbose(`[Maven Analyzer] Verbose mode: ${isVerbose}`);
    devkit_1.logger.verbose(`[Maven Analyzer] Workspace root: ${workspaceRoot}`);
    devkit_1.logger.verbose(`[Maven Analyzer] Workspace data directory: ${cache_directory_1.workspaceDataDirectory}`);
    // Detect Maven executable (mvnd > mvnw > mvn)
    const mavenExecutable = (0, detect_maven_executable_1.detectMavenExecutable)(workspaceRoot);
    const mavenArgs = [
        'dev.nx.maven:nx-maven-plugin:analyze',
        '-am',
        `-DoutputFile=${outputFile}`,
        `-DworkspaceRoot=${workspaceRoot}`,
        '--batch-mode',
        '--no-transfer-progress',
    ];
    if (options.targetNamePrefix) {
        mavenArgs.push(`-DtargetNamePrefix=${options.targetNamePrefix}`);
    }
    if (!isVerbose) {
        mavenArgs.push('-q');
    }
    devkit_1.logger.verbose(`[Maven Analyzer] Maven command: ${mavenExecutable} ${mavenArgs.join(' ')}`);
    devkit_1.logger.verbose(`[Maven Analyzer] Working directory: ${workspaceRoot}`);
    // Debug logging for verbose mode
    if (isVerbose) {
        console.error(`Running Maven analyzer with verbose logging: ${mavenExecutable} ${mavenArgs.join(' ')}`);
    }
    // Cancel any in-flight Maven process from a previous call, then create a fresh controller.
    cancelPendingMavenAnalysis();
    const controller = new AbortController();
    currentAbortController = controller;
    const signal = controller.signal;
    const timeoutMs = getAnalysisTimeoutMs();
    const timeoutSeconds = timeoutMs / 1000;
    const timer = setTimeout(() => controller.abort(), timeoutMs);
    // Run Maven plugin
    devkit_1.logger.verbose(`[Maven Analyzer] Spawning Maven process...`);
    try {
        await new Promise((resolve, reject) => {
            const child = (0, child_process_1.spawn)(mavenExecutable, mavenArgs, {
                cwd: workspaceRoot,
                windowsHide: true,
                shell: true,
                stdio: 'pipe', // Always use pipe so we can control output
            });
            // Use tree-kill on abort to kill the entire process tree
            const onAbort = () => {
                if (child.pid) {
                    treeKill(child.pid);
                }
            };
            signal.addEventListener('abort', onAbort, { once: true });
            devkit_1.logger.verbose(`[Maven Analyzer] Process spawned with PID: ${child.pid}`);
            let stdout = '';
            let stderr = '';
            // In verbose mode, forward output to console in real-time
            if (isVerbose) {
                child.stdout?.on('data', (data) => {
                    const text = data.toString();
                    stdout += text;
                    process.stdout.write(text); // Forward to stdout
                });
                child.stderr?.on('data', (data) => {
                    const text = data.toString();
                    stderr += text;
                    process.stderr.write(text); // Forward to stderr
                });
            }
            else {
                child.stdout?.on('data', (data) => {
                    const text = data.toString();
                    stdout += text;
                    devkit_1.logger.verbose(`[Maven Analyzer] Stdout chunk: ${text.trim()}`);
                });
                child.stderr?.on('data', (data) => {
                    const text = data.toString();
                    stderr += text;
                    devkit_1.logger.verbose(`[Maven Analyzer] Stderr chunk: ${text.trim()}`);
                });
            }
            child.on('close', (code) => {
                signal.removeEventListener('abort', onAbort);
                devkit_1.logger.verbose(`[Maven Analyzer] Process closed with code: ${code}`);
                if (code === 0) {
                    devkit_1.logger.verbose(`[Maven Analyzer] Maven analysis completed successfully`);
                    resolve();
                }
                else {
                    let errorMsg = `Maven analysis failed with code ${code}`;
                    if (stderr)
                        errorMsg += `\nStderr: ${stderr}`;
                    if (stdout && !isVerbose)
                        errorMsg += `\nStdout: ${stdout}`;
                    console.error(`[Maven Analyzer] Error: ${errorMsg}`);
                    reject(new Error(errorMsg));
                }
            });
            child.on('error', (error) => {
                signal.removeEventListener('abort', onAbort);
                console.error(`[Maven Analyzer] Process error: ${error.message}`);
                reject(new Error(`Failed to spawn Maven process: ${error.message}`));
            });
        });
    }
    catch (e) {
        if (signal.reason === 'cancelled') {
            throw new Error('Maven analysis was cancelled');
        }
        if (signal.aborted) {
            throw new Error(`Maven analysis timed out after ${timeoutSeconds} ${timeoutSeconds === 1 ? 'second' : 'seconds'}.\n` +
                `  1. If the issue persists, set the environment variable NX_MAVEN_ANALYSIS_TIMEOUT to a higher value (in seconds) to increase the timeout.\n` +
                `  2. If the issue still persists, set NX_MAVEN_DISABLE=true to disable the Maven plugin entirely.`);
        }
        throw e;
    }
    finally {
        clearTimeout(timer);
    }
    // Read and parse the JSON output
    devkit_1.logger.verbose(`[Maven Analyzer] Checking for output file: ${outputFile}`);
    if (!(0, fs_1.existsSync)(outputFile)) {
        console.error(`[Maven Analyzer] Output file not found: ${outputFile}`);
        throw new Error(`Maven analysis output file not found: ${outputFile}`);
    }
    const result = (0, devkit_1.readJsonFile)(outputFile);
    devkit_1.logger.verbose(`[Maven Analyzer] Output file size: ${result.length} characters`);
    return result;
}
