import { getNormalizedAbsolutePath, NoTarget, resolvePath, } from "@typespec/compiler";
import { promises } from "fs";
import { dump } from "js-yaml";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { CodeModelBuilder } from "./code-model-builder.js";
import { LibName, reportDiagnostic } from "./lib.js";
import { DiagnosticError, spawnAsync, SpawnError, trace } from "./utils.js";
import { validateDependencies } from "./validate.js";
export async function $onEmit(context) {
    const program = context.program;
    if (!program.compilerOptions.noEmit) {
        await validateDependencies(program, true);
    }
    if (!program.hasError()) {
        const options = context.options;
        if (!options["flavor"]) {
            if (LibName === "@azure-tools/typespec-java") {
                options["flavor"] = "azure";
            }
        }
        let codeModel;
        try {
            const builder = new CodeModelBuilder(program, context);
            codeModel = await builder.build();
        }
        catch (error) {
            if (error instanceof DiagnosticError) {
                // diagnostic thrown as error
                program.reportDiagnostic(error.diagnostic);
            }
            else {
                // unknown error
                reportDiagnostic(program, {
                    code: "unknown-error",
                    format: {
                        errorMessage: `Error occurred when building the code model. The emitter was unable to generate client code from this TypeSpec, please open an issue on https://github.com/microsoft/typespec, include TypeSpec source and all the diagnostic information in your submission.\nStack: ${error.stack}\nError: ${error.toString()}`,
                    },
                    target: NoTarget,
                });
                trace(program, error.stack);
            }
        }
        if (codeModel && !program.hasError() && !program.compilerOptions.noEmit) {
            const __dirname = dirname(fileURLToPath(import.meta.url));
            const moduleRoot = resolvePath(__dirname, "..", "..");
            const outputPath = context.emitterOutputDir;
            options["output-dir"] = getNormalizedAbsolutePath(outputPath, undefined);
            options.arm = codeModel.arm;
            if (codeModel.info?.license?.extensions?.header) {
                options["license-header"] = codeModel.info.license.extensions.header;
            }
            const codeModelFileName = resolvePath(outputPath, "./code-model.yaml");
            await promises.mkdir(outputPath, { recursive: true }).catch((err) => {
                if (err.code !== "EISDIR" && err.code !== "EEXIST") {
                    reportDiagnostic(program, {
                        code: "unknown-error",
                        format: { errorMessage: `Failed to create output directory: ${outputPath}.` },
                        target: NoTarget,
                    });
                    return;
                }
            });
            await program.host.writeFile(codeModelFileName, dump(codeModel));
            trace(program, `Code model file written to ${codeModelFileName}`);
            const emitterOptions = JSON.stringify(options);
            trace(program, `Emitter options ${emitterOptions}`);
            const jarFileName = resolvePath(moduleRoot, "generator/http-client-generator/target", "emitter.jar");
            trace(program, `Exec JAR ${jarFileName}`);
            const javaArgs = [];
            javaArgs.push(`-DemitterOptions=${emitterOptions}`);
            if (options["dev-options"]?.debug) {
                javaArgs.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005");
            }
            if (options["dev-options"]?.loglevel) {
                javaArgs.push("-Dorg.slf4j.simpleLogger.defaultLogLevel=" + options["dev-options"]?.loglevel);
            }
            if (options["dev-options"]?.["java-temp-dir"]) {
                javaArgs.push("-Dcodegen.java.temp.directory=" + options["dev-options"]?.["java-temp-dir"]);
            }
            if (options["dev-options"]?.profile) {
                const perfProfile = resolvePath(moduleRoot, "generator/http-client-generator/target/classes", "PerfAutomation.jfc");
                javaArgs.push("-XX:+FlightRecorder");
                javaArgs.push(`-XX:StartFlightRecording=settings="${perfProfile}",filename="${options["output-dir"]}/typespecPerf.jfr",maxsize=1gb`);
            }
            // These module modifications are needed by google-java-format
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED");
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED");
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED");
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED");
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED");
            javaArgs.push("--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED");
            javaArgs.push("-jar");
            javaArgs.push(jarFileName);
            javaArgs.push(codeModelFileName);
            try {
                const result = await spawnAsync("java", javaArgs, { stdio: "pipe" });
                reportJarOutput(program, result.stdout);
                // trace(program, `Code generation log: ${result.stdout}`);
            }
            catch (error) {
                if (error && "code" in error && error["code"] === "ENOENT") {
                    reportDiagnostic(program, {
                        code: "invalid-java-sdk-dependency",
                        target: NoTarget,
                    });
                }
                else {
                    if (error instanceof SpawnError) {
                        reportJarOutput(program, error.stdout);
                        // trace(program, `Code generation log: ${error.stdout}`);
                    }
                    // error in Java codegen, report as unknown error
                    reportDiagnostic(program, {
                        code: "unknown-error",
                        format: {
                            errorMessage: `Error occurred while running Java generator. The emitter was unable to generate client code from this TypeSpec, please open an issue on https://github.com/microsoft/typespec, include TypeSpec source and all the diagnostic information in your submission. ${error.stack}\nError: ${error.toString()}`,
                        },
                        target: NoTarget,
                    });
                }
            }
            if (!options["dev-options"]?.["generate-code-model"]) {
                await program.host.rm(codeModelFileName);
            }
        }
    }
}
function reportJarOutput(program, jarOutput) {
    const lines = jarOutput.split("\n");
    const logs = [];
    // parse stdout to array of logs
    let currentLog = undefined;
    for (const line of lines) {
        if (line.startsWith("TRACE ") ||
            line.startsWith("DEBUG ") ||
            line.startsWith("INFO ") ||
            line.startsWith("WARN ") ||
            line.startsWith("ERROR ")) {
            if (currentLog) {
                logs.push(currentLog);
            }
            currentLog = line;
        }
        else if (currentLog) {
            currentLog = currentLog + "\n" + line;
        }
    }
    if (currentLog) {
        logs.push(currentLog);
    }
    // trace or report the logs, according to log level
    const typeSpecPluginPrefix = "com.microsoft.typespec.http.client.generator.";
    const errorPrefix = "ERROR " + typeSpecPluginPrefix;
    const warnPrefix = "WARN " + typeSpecPluginPrefix;
    for (const log of logs) {
        if (log.startsWith(errorPrefix)) {
            reportDiagnostic(program, {
                code: "generator-error",
                format: {
                    errorMessage: log.substring(errorPrefix.length),
                },
                target: NoTarget,
            });
        }
        else if (log.startsWith(warnPrefix)) {
            reportDiagnostic(program, {
                code: "generator-warning",
                format: {
                    warningMessage: log.substring(warnPrefix.length),
                },
                target: NoTarget,
            });
        }
        else {
            const index = log.indexOf(" ");
            trace(program, log.substring(index + 1));
        }
    }
}
//# sourceMappingURL=emitter.js.map