import constants from "../constants.js";
import DockerManager from "../docker/dockerManager.js";
import Volume from "../docker/volume.js";
import ImagePuller from "../images/imagePuller.js";
import OutputManager from "../outputManager.js";
import PackageConfig from "../packageConfig.js";
import RunSpecConfig from "../runSpecConfig.js";
import StatusManager from "../statusManager.js";
import ScriptBuilder from "./scriptBuilder.js";
import ScriptPackager from "./scriptPackager.js";
export default class ScriptRunner {
    noBuildArtifactCache;
    runSpecName;
    srcPath;
    constructor(srcPath, runSpecName = constants.DEFAULT_RUNSPEC_NAME, noBuildArtifactCache = false) {
        this.noBuildArtifactCache = noBuildArtifactCache;
        this.runSpecName = runSpecName;
        this.srcPath = srcPath;
    }
    async run() {
        const parsedRunSpecConfig = new RunSpecConfig(this.srcPath, this.runSpecName).parse();
        // Apply fallback logic for workDir and scriptDir
        parsedRunSpecConfig.scriptDir = parsedRunSpecConfig.scriptDir || constants.DEFAULT_SCRIPT_WORK_DIR;
        parsedRunSpecConfig.workDir = parsedRunSpecConfig.workDir || parsedRunSpecConfig.scriptDir || constants.DEFAULT_SCRIPT_WORK_DIR;
        const packageConfig = new PackageConfig(this.srcPath);
        const parsedPackageConfig = packageConfig.isAvailable() ? packageConfig.parse() : null;
        const parsedBuildConfig = parsedPackageConfig?.build;
        const docker = await DockerManager.get();
        const buildImage = (await (new ScriptBuilder(this.srcPath)).build());
        let srcPath;
        let filePatterns;
        let executionImage;
        if (!parsedBuildConfig || this.noBuildArtifactCache || parsedBuildConfig.image !== parsedRunSpecConfig.image) {
            srcPath = await (new ScriptPackager(this.srcPath, this.runSpecName, buildImage)).stage();
            filePatterns = ["**/*"];
            executionImage = await new ImagePuller(docker, parsedRunSpecConfig.image).pull();
        }
        else {
            srcPath = this.srcPath;
            filePatterns = parsedPackageConfig?.src?.filter(srcItem => srcItem !== null) || [];
            executionImage = buildImage;
        }
        let mountConfig = {};
        if (parsedPackageConfig) {
            mountConfig = await this.deployScript(docker, srcPath, parsedRunSpecConfig.scriptDir, filePatterns);
        }
        await this.executeScript(executionImage, parsedRunSpecConfig, mountConfig);
    }
    async deployScript(docker, srcPath, destPath, filePatterns) {
        StatusManager.startTask("Deploying script to runtime environment");
        const volume = new Volume(docker);
        const mountConfig = { [destPath]: volume };
        await volume.create();
        await volume.put(srcPath, filePatterns, destPath);
        return mountConfig;
    }
    async executeScript(executorImage, parsedRunSpecConfig, mountConfig) {
        const imageName = executorImage.getName();
        StatusManager.startTask(`Executing script`);
        OutputManager.detail(`Executing run spec "${this.runSpecName}" in image "${imageName}"`);
        try {
            await executorImage.withContainer({
                command: parsedRunSpecConfig.command,
                egress: parsedRunSpecConfig.egress,
                env: parsedRunSpecConfig.env,
                mounts: mountConfig,
                pwd: parsedRunSpecConfig.workDir,
            }, async (container) => {
                OutputManager.outputDivider("BEGIN SCRIPT OUTPUT");
                const exitCode = await container.run();
                OutputManager.outputDivider("END SCRIPT OUTPUT");
                if (exitCode !== 0) {
                    throw new Error(`Script execution failed with exit code ${exitCode}`);
                }
            });
        }
        finally {
            if (mountConfig && parsedRunSpecConfig.scriptDir) {
                const volume = mountConfig[parsedRunSpecConfig.scriptDir];
                if (volume) {
                    await volume.destroy();
                }
            }
        }
    }
}
