import { glob } from 'glob';
import fs from 'node:fs';
import path from 'node:path';
import { pipeline } from 'node:stream';
import { promisify } from 'node:util';
import zlib from 'node:zlib';
import tar from 'tar-fs';
import OutputManager from '../outputManager.js';
import PackageConfig from '../packageConfig.js';
import RandomString from '../randomString.js';
import StatusManager from '../statusManager.js';
import TempDirManager from '../tempDirManager.js';
export default class ScriptPackager {
    _REQUIRED_SOURCE_FILES = ["config/**/*"];
    buildImage;
    scriptNameBase;
    scriptPath;
    stagingDir;
    constructor(scriptPath, scriptName, buildImage) {
        this.scriptPath = scriptPath;
        this.buildImage = buildImage;
        this.scriptNameBase = `${scriptName}-${RandomString.get()}`;
        this.stagingDir = TempDirManager.get(`${scriptName}-staging`);
    }
    async package() {
        StatusManager.startTask('Packaging script');
        const packagePath = await this.createPackage();
        OutputManager.newline();
        OutputManager.detail(`Created package at ${packagePath}`);
        StatusManager.finishTask();
        return packagePath;
    }
    async stage() {
        StatusManager.startTask("Staging script files");
        const packageConfig = new PackageConfig(this.scriptPath);
        let parsedPackageConfig;
        let artifacts;
        if (packageConfig.isAvailable()) {
            parsedPackageConfig = packageConfig.parse();
            artifacts = parsedPackageConfig?.build?.artifacts || {};
        }
        else {
            parsedPackageConfig = {};
            artifacts = {};
        }
        const hasArtifacts = artifacts && Object.keys(artifacts).length > 0;
        if (hasArtifacts && !this.buildImage) {
            throw new Error("Build image is required to stage artifacts.");
        }
        const sourceFiles = [...this._REQUIRED_SOURCE_FILES, ...(parsedPackageConfig?.src || [])];
        OutputManager.detail("Staging source files");
        this.stageSrcFiles(sourceFiles);
        OutputManager.newline();
        OutputManager.detail("Staging artifacts");
        if (Object.keys(artifacts).length > 0) {
            await this.stageArtifacts(artifacts);
        }
        StatusManager.finishTask();
        return this.stagingDir;
    }
    async createPackage() {
        const packagePath = path.join(TempDirManager.get(), `${this.scriptNameBase}.tgz`);
        const pipelineAsync = promisify(pipeline);
        const output = fs.createWriteStream(packagePath);
        await pipelineAsync(tar.pack(this.stagingDir), zlib.createGzip(), output);
        return packagePath;
    }
    async stageArtifacts(artifactPaths) {
        await this.buildImage.getFiles(this.stagingDir, artifactPaths);
    }
    stageSrcFiles(srcPathPatterns) {
        const srcFilePaths = new Set();
        for (const pattern of srcPathPatterns) {
            const files = glob.sync(pattern, { cwd: this.scriptPath });
            for (const file of files) {
                const srcPath = path.join(this.scriptPath, file);
                const destPath = path.join(this.stagingDir, file);
                const destDir = path.dirname(destPath);
                if (fs.statSync(srcPath).isDirectory()) {
                    OutputManager.detail(`Creating directory: ${file}`);
                    fs.mkdirSync(destPath, { recursive: true });
                }
                else {
                    fs.mkdirSync(destDir, { recursive: true });
                    OutputManager.detail(`Staging file: ${file} => ${destPath}`);
                    fs.copyFileSync(path.join(this.scriptPath, file), destPath);
                }
            }
        }
        return [...srcFilePaths];
    }
}
