"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = exports.isVerison = exports.JSONparse = exports.readFile = exports.sleep = exports.waitGC = exports.input = void 0;
exports.hasKeys = hasKeys;
exports.zip = zip;
exports.toString = toString;
exports.ToArray = ToArray;
exports.readFileWithRetry = readFileWithRetry;
exports.join = join;
exports.Exit = Exit;
exports.FileExsit = FileExsit;
exports.GetData = GetData;
exports.isMblerProject = isMblerProject;
exports.handlerPackage = handlerPackage;
exports.copy = copy;
const node_path_1 = __importDefault(require("node:path"));
const adm_zip_1 = __importDefault(require("adm-zip"));
const index_js_1 = __importDefault(require("./../loger/index.js"));
const commander_1 = require("./../commander");
const config = require('./../build/build-g-config.json');
const promises_1 = __importDefault(require("node:fs/promises"));
function hasKeys(obj, keys, minValue) {
    if (!(typeof obj === `object` &&
        !Array.isArray(obj)))
        return false;
    if (!Array.isArray(keys))
        return false;
    let count = 0;
    for (const key of keys) {
        if (obj.hasOwnProperty(key)) {
            count++;
            if (count >= minValue)
                return true;
        }
    }
    return false;
}
exports.input = function () {
    let curr;
    let currstr = "";
    let tip = "";
    let show = true;
    // 在输入时使用输入中间件
    commander_1.Input.use(function (raw, ctrl, alt, name) {
        if (typeof curr !== "function")
            return;
        if (ctrl || alt)
            return;
        if (raw) {
            if (raw === 'return' || raw === 'enter') {
                curr(currstr);
                curr = null;
                currstr = "";
                console.log("");
                return;
            }
            if (raw === 'backspace') {
                currstr = currstr.slice(0, -1);
                refreshInput();
                return;
            }
        }
        if (name && typeof name === 'string') {
            currstr += name[0];
            refreshInput();
        }
    });
    function refreshInput() {
        const out = `\x1b[2K\r${tip}${show ? currstr : ""}`;
        process.stdout.write(out);
    }
    /**
     * 输入文本
     * @param{string} tip 提示
     * @param{boolean} show 是否显示输入
     */
    return async function (t = "", g = true) {
        return new Promise((resolve) => {
            show = g;
            tip = t;
            refreshInput();
            curr = resolve;
        });
    };
}();
/**
 * 压缩目录为 ZIP，解压后根目录是 sourceDir 内的内容，而不是 sourceDir 文件夹本身
 * @param {Array} sourceDir 要压缩的目录路径，如 ['C:/data/myfolder' 或 '/home/user/myfolder']
 * @param {string} outputFile 输出的 ZIP 文件完整路径，如 'C:/backup/output.zip' 或 '/backup/output.zip'
 * @returns {Promise<boolean>} 是否压缩成功
 */
async function zip(sourceDir, outputFile) {
    const zip = new adm_zip_1.default();
    for (let item of sourceDir) {
        if (typeof item !== "string")
            continue;
        zip.addLocalFolder(item);
    }
    await zip.writeZipPromise(outputFile);
}
const waitGC = () => new Promise(r => setImmediate(r));
exports.waitGC = waitGC;
function toString(t) {
    if (typeof t === 'string')
        return t;
    if (t instanceof Error) {
        if (typeof t.stack == "string")
            return t.stack;
        return t.message;
    }
    ;
    if (Array.isArray(t))
        return t.map((item) => toString(item)).join('\n');
    if (typeof t?.toString === 'function')
        return t.toString();
    if (t === void 0)
        return '[Objeect utils]';
    return JSON.stringify(t, null, 2);
}
function ToArray(str) {
    return str.split(`.`).map(Number);
}
const sleep = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms));
exports.sleep = sleep;
/**
 * 读取文件内容
 * @param filePath 文件路径
 * @param opt 选项，包含 want: 'string' | 'object'
 * @returns 文件内容（字符串或解析后的对象）
 */
async function readFileWithRetry(filePath, opt) {
    const opts = {
        maxRetries: 5,
        delay: 200,
        want: 'string',
        ...opt
    };
    let lastError;
    for (let attempt = 0; attempt < opts.maxRetries; attempt++) {
        try {
            let resu = "";
            const text = (await promises_1.default.readFile(filePath)).toString();
            if (opts.want === 'string')
                resu = text.toString();
            if (opts.want === 'object') {
                try {
                    resu = JSON.parse(text);
                }
                catch {
                    resu = {};
                }
            }
            return resu;
        }
        catch (err) {
            lastError = err;
            if (attempt < opts.maxRetries - 1) {
                await (0, exports.sleep)(opts.delay);
            }
        }
    }
    return {};
}
exports.readFile = readFileWithRetry;
const JSONparse = function (str) {
    return JSON.parse(str);
};
exports.JSONparse = JSONparse;
function join(baseDir, inputPath) {
    return node_path_1.default.isAbsolute(inputPath) ?
        inputPath :
        node_path_1.default.join(baseDir, inputPath);
}
const isVerison = function (str) {
    return /\d+\.\d+\.\d+/.test(str);
};
exports.isVerison = isVerison;
function Exit(msg) {
    index_js_1.default.e('ERROR', msg);
    process.exit(1);
}
async function FileExsit(dir) {
    try {
        await promises_1.default.access(dir);
        return true;
    }
    catch (err) {
        return false;
    }
}
async function GetData(dir) {
    const configPath = node_path_1.default.join(dir, config.PackageFile);
    const fileContent = await promises_1.default.readFile(configPath, 'utf-8');
    const data = (0, exports.JSONparse)(fileContent);
    if (typeof data.version === "string" && typeof data.name === "string" && typeof data.description === "string")
        return data;
    throw new Error("[mbler find config]: " + dir + ": not found config");
}
async function isMblerProject(Dir) {
    const rel = await Promise.all([
        node_path_1.default.join(Dir, "package.json"),
        node_path_1.default.join(Dir, config.PackageFile)
    ].map(FileExsit)).catch((e) => {
        console.log(e.stack);
        return [false, false];
    });
    const res = rel.every(Boolean);
    return res;
}
/**
 * 检查版本号是否有效（有效返回 true）
 * @param {any} v
 * @returns {boolean}
 */
function isValidVersion(v) {
    return typeof v === 'string' && (0, exports.isVerison)(v);
}
/**
 * 处理模块包配置，提取依赖、主入口、UI 配置等
 * @param {string} dir 模块目录路径
 * @param {Object} opt 可选参数（如：已处理的依赖列表）
 * @returns {Promise<Object>} { des: string[], ui: boolean, main: string }
 */
async function handlerPackage(dir, opt = {
    des: []
}) {
    let data;
    try {
        data = await GetData(dir);
    }
    catch (err) {
        throw new Error(`无法读取模块配置文件: ${node_path_1.default.join(dir, config.PackageFile)}`);
    }
    // 必需字段校验
    if (typeof data.name !== 'string' ||
        typeof data.description !== 'string' ||
        !isValidVersion(data.version)) {
        throw new Error(`ERR-\n${node_path_1.default.join(dir, config.PackageFile)} 缺少必要字段。\n` +
            '必需字段: name, description, version');
    }
    const result = {
        des: {}, // 依赖模块名数组
        ui: false, // 是否使用 @minecraft/server-ui
        main: './index.js', // 默认主入口
        name: data.name,
        version: data.version,
        description: data.description
    };
    // opt.des : Type Array || undefined
    if (opt.des && data?.script?.dependencies)
        result.des = ForOfMod(data.script.dependencies, opt.des);
    result.ui = Boolean(Boolean(data?.script?.ui));
    if (data?.script) {
        result.main = data.script.main;
    }
    return result;
}
;
/**
 *  * 遍历模块依赖配置，过滤掉已经安装的模块
 * @param Mod 模块依赖对象，key 是包名，value 是 Git 地址，例如 { "module-a": "git-url" }
 * @param InstallMod 已安装的模块名数组，例如 ["module-a", "module-b"]
 * @returns 过滤后的模块对象，只包含未安装的模块
 */
function ForOfMod(Mod, // 比如 { "pkg": "git-url" }
InstallMod // 比如 ["pkg1", "pkg2"]
) {
    let returnValue = {};
    try {
        for (const [packageName, gitRepo] of Object.entries(Mod)) {
            if (InstallMod?.includes &&
                typeof InstallMod.includes === 'function' &&
                InstallMod.includes(packageName)) {
                continue; // 如果已安装，跳过
            }
            returnValue[packageName] = gitRepo;
        }
    }
    catch (err) {
        console.error('ForOfMod 发生错误:', err);
    }
    return returnValue;
}
async function copy(src, out) {
    try {
        await promises_1.default.cp(src, out, {
            force: true,
            recursive: true
        });
    }
    catch (err) { }
}
;
const iconfig = require("./../build/build-g-config.json");
exports.config = iconfig;
