"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseBuild = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const dayjs_1 = __importDefault(require("dayjs"));
const index_js_1 = __importDefault(require("./../lang/index.js"));
const index_js_2 = __importDefault(require("./../runTemp/index.js"));
const child_process_1 = require("child_process");
const manifest_build_js_1 = require("./manifest.build.js");
const node_os_1 = __importDefault(require("node:os"));
const time = () => (0, dayjs_1.default)().format('YYYY-MM-DD HH:mm:ss');
const utils_1 = __importDefault(require("./utils"));
const utils = __importStar(require("./../utils/index.js"));
// 这里是主Build的底类，用于提供工具，主Build负责将工具连起来，进行完整的构建
class BaseBuild {
    static times = time();
    outdir = "";
    ResOutDir = null;
    baseCwd = '';
    cwd = '';
    ResCwd = '';
    d_data = null;
    cacheDir = '';
    dependencies = {};
    Modules = [];
    constructor() { }
    async rmPackScriptScriptCache() {
        await Promise.all([
            this.rmNpmDes(),
            promises_1.default.rm(path_1.default.join(this.outdir, 'scripts/package-lock.json'), {
                recursive: true,
                force: true
            }).catch(() => { })
        ]);
    }
    async processResources() {
        if ((await utils.FileExsit(this.ResCwd)) && this.ResOutDir) {
            await Promise.all((await promises_1.default.readdir(this.ResCwd)).map((dir) => utils.copy(path_1.default.join(this.ResCwd, dir), path_1.default.join(this.ResOutDir, dir))));
            if (this.d_data == null)
                throw new Error("[build error]: not load config when init");
            const manifest = (new manifest_build_js_1.ManiFest(this.d_data, "resources")).data;
            await this.writeFile(path_1.default.join(this.ResOutDir, "manifest.json"), JSON.stringify(manifest));
        }
        else {
            (0, utils_1.default)(index_js_1.default.build.no_resources);
        }
    }
    async processDist() {
        if (process.env.MBLER_BUILD_MODULE === "dist") {
            const temp = new index_js_2.default(path_1.default.join(node_os_1.default.tmpdir(), "mbler"));
            await temp.init();
            await Promise.all([
                utils.copy(this.outdir, path_1.default.join(temp.dir, "behavior")),
                (await utils.FileExsit(this.ResCwd)) ?
                    utils.copy(this.ResOutDir, path_1.default.join(temp.dir, "resources")) :
                    Promise.resolve()
            ]);
            let i = this.d_data?.outdir?.dist || "dist.mcaddon";
            if (path_1.default.extname(i) !== ".mcaddon")
                i += ".mcaddon";
            const dir = path_1.default.join(this.baseCwd, i);
            const v = require('./../../package.json');
            await promises_1.default.writeFile(path_1.default.join(temp.dir, "behavior", ".mbler.build.info"), JSON.stringify({
                mbler: {
                    version: v.version,
                    git: v.repository
                },
                time: BaseBuild.times
            }));
            await utils.zip([
                temp.dir
            ], dir);
            await temp.remove();
            (0, utils_1.default)(`${index_js_1.default.build.ziped} ${dir}`);
        }
    }
    async loadPackageData() {
        const data = await utils.GetData(this.baseCwd);
        if (!data)
            throw new Error(index_js_1.default.buildBase.cannot_read_project_config);
        if (typeof data !== 'object' || data === null)
            throw new Error(index_js_1.default.buildBase.invalid_project_config_format);
        return data;
    }
    getCachePath(fileName) {
        return path_1.default.join(this.cacheDir, fileName);
    }
    getOutputDir(dir, def) {
        const outdir = dir;
        return outdir ? path_1.default.join(this.baseCwd, outdir) : def;
    }
    async initNpmDes() {
        const packager = JSON.parse(await promises_1.default.readFile(path_1.default.join(this.baseCwd, 'package.json'), "utf-8"));
        const desLength = Object.keys(packager.dependencies || {}).length;
        if (desLength < 1) {
            (0, utils_1.default)(index_js_1.default.buildBase.npm_deps_skipped_no_json);
            return;
        }
        await promises_1.default.writeFile(path_1.default.join(this.outdir, 'scripts/package.json'), JSON.stringify(packager));
        await this.#npmInstall(path_1.default.join(this.outdir, 'scripts'));
    }
    #npmInstall(repo) {
        return new Promise((resolve, reject) => {
            (0, utils_1.default)(index_js_1.default.buildBase.starting_npm_install);
            const processC = (0, child_process_1.spawn)('npm', ['install'], {
                cwd: repo,
                stdio: 'ignore'
            });
            processC.on('close', (code) => {
                if (code === 0) {
                    (0, utils_1.default)(index_js_1.default.buildBase.npm_install_completed);
                    resolve(code);
                }
                else {
                    (0, utils_1.default)(index_js_1.default.buildBase.npm_install_exit_code + code);
                    resolve(code);
                }
            });
            processC.on('error', (err) => {
                (0, utils_1.default)(`${index_js_1.default.buildBase.npm_install_error} ${err.stack}`);
                reject(err);
            });
        });
    }
    async getFileHash(filePath) {
        if (!await utils.FileExsit(filePath))
            return null;
        const content = await promises_1.default.readFile(filePath);
        const hashSum = (await import('crypto')).createHash('sha1');
        hashSum.update(content);
        return hashSum.digest('hex');
    }
    async chackConfigHash() {
        const configPath = path_1.default.join(this.cwd, 'mbler.config.json');
        const packagePath = path_1.default.join(this.cwd, 'package.json');
        const configHashPath = this.getCachePath('config-hash.txt');
        const packageHashPath = this.getCachePath('package-hash.txt');
        const currentConfigHash = await this.getFileHash(configPath);
        const currentPackageHash = await this.getFileHash(packagePath);
        let configHashStored = null;
        let packageHashStored = null;
        if (await promises_1.default.access(configHashPath).then(() => true).catch(() => false)) {
            configHashStored = await promises_1.default.readFile(configHashPath, "utf-8");
        }
        if (await promises_1.default.access(packageHashPath).then(() => true).catch(() => false)) {
            packageHashStored = await promises_1.default.readFile(packageHashPath, "utf-8");
        }
        const configChanged = !currentConfigHash || currentConfigHash !== configHashStored;
        const packageChanged = !currentPackageHash || currentPackageHash !== packageHashStored;
        if (currentConfigHash) {
            await promises_1.default.writeFile(configHashPath, currentConfigHash);
        }
        if (currentPackageHash) {
            await promises_1.default.writeFile(packageHashPath, currentPackageHash);
        }
        return {
            configChanged,
            packageChanged
        };
    }
    async removeJsFiles(dir) {
        try {
            for (const f of await promises_1.default.readdir(dir, {
                withFileTypes: true
            })) {
                const full = path_1.default.join(dir, f.name);
                if (full.includes('node_modules'))
                    continue;
                if (f.isDirectory()) {
                    await this.removeJsFiles(full);
                }
                else if (f.isFile() && f.name.endsWith('.js')) {
                    try {
                        await promises_1.default.unlink(full);
                    }
                    catch (e) {
                        /* 忽略 unlink 错误 */
                    }
                }
            }
        }
        catch (err) { }
    }
    async rmNpmDes() {
        const moduleDir = path_1.default.join(this.outdir, 'scripts/node_modules');
        const modules = this.Modules || Object.keys(this.dependencies);
        for (let file of await promises_1.default.readdir(moduleDir).catch(() => [])) {
            if (!modules.includes(file))
                await promises_1.default.rm(path_1.default.join(moduleDir, file), {
                    recursive: true,
                    force: true
                }).catch(() => { });
        }
    }
    async copyCompiledOnly(srcDir, destDir) {
        try {
            for (const ent of await promises_1.default.readdir(srcDir, {
                withFileTypes: true
            })) {
                const srcFull = path_1.default.join(srcDir, ent.name);
                const destFull = path_1.default.join(destDir, ent.name);
                if (ent.isDirectory()) {
                    await promises_1.default.mkdir(destFull, {
                        recursive: true
                    }).catch(() => { });
                    await this.copyCompiledOnly(srcFull, destFull);
                }
                else if (ent.isFile()) {
                    const ext = path_1.default.extname(ent.name).toLowerCase();
                    if ([".js", ".json"].includes(ext)) {
                        await promises_1.default.mkdir(path_1.default.dirname(destFull), {
                            recursive: true
                        }).catch(() => { });
                        await promises_1.default.copyFile(srcFull, destFull).catch(() => {
                            // 文件可能被占用，忽略单个错误
                        });
                    }
                }
            }
        }
        catch (err) {
            (0, utils_1.default)(`ERR: ${err && err.stack ? err.stack : err}`);
        }
    }
    async getAllTsFiles(dir) {
        const list = [];
        try {
            for (const f of await promises_1.default.readdir(dir, {
                withFileTypes: true
            })) {
                const full = path_1.default.join(dir, f.name);
                if (f.isDirectory()) {
                    const sub = await this.getAllTsFiles(full);
                    list.push(...sub);
                }
                else if (f.isFile() && f.name.endsWith('.ts')) {
                    list.push(full);
                }
            }
        }
        catch (err) { }
        return list;
    }
    async writeFile(filePath, content) {
        try {
            await utils.waitGC();
            await promises_1.default.mkdir(path_1.default.dirname(filePath), {
                recursive: true
            });
            const data = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
            await promises_1.default.writeFile(filePath, data, 'utf-8');
        }
        catch (err) {
            (0, utils_1.default)(`WRITE FILE ERR: ${filePath}`);
            throw err;
        }
        await utils.waitGC();
    }
}
exports.BaseBuild = BaseBuild;
