"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __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.MoveBuilder = void 0;
/* eslint-disable @typescript-eslint/unbound-method */
const ref_napi_1 = __importDefault(require("@eleccookie/ref-napi"));
const path = __importStar(require("path"));
const promises_1 = require("fs/promises");
const lib_1 = require("./lib");
const types_1 = require("./types");
class MoveBuilder {
    packagePath;
    buildOptions;
    /**
     * Create a MoveBuilder.
     * @param packagePath - Full path to package directory.
     * @param buildOptions - Move package build options.
     */
    constructor(packagePath, buildOptions) {
        this.packagePath = packagePath;
        this.buildOptions = buildOptions;
    }
    /**
     * Create raw build configuration.
     * @returns Raw build configuration.
     */
    makeRawBuildConfig = () => {
        const additionalNamedAddresses = this.buildOptions
            .additionalNamedAddresses
            ? this.buildOptions.additionalNamedAddresses.map(([name, address]) => {
                if (address.startsWith('0x')) {
                    address = address.slice(2).padStart(64, '0');
                }
                return [name, Buffer.from(address, 'hex')];
            })
            : [];
        const compilerPayloadBytes = Buffer.from(types_1.compilerPayloadBcsType
            .serialize({
            package_path: this.packagePath,
            verbose: false,
            build_config: {
                dev_mode: this.buildOptions.devMode || false,
                test_mode: this.buildOptions.testMode || false,
                generate_docs: this.buildOptions.generateDocs || false,
                generate_abis: this.buildOptions.generateAbis || false,
                install_dir: this.buildOptions.installDir || '',
                force_recompilation: this.buildOptions.forceRecompilation || false,
                fetch_deps_only: this.buildOptions.fetchDepsOnly || false,
                skip_fetch_latest_git_deps: this.buildOptions.skipFetchLatestGitDeps || false,
                bytecode_version: this.buildOptions.bytecodeVersion || 0,
                compiler_version: this.buildOptions.compilerVersion || '0',
                language_version: this.buildOptions.languageVersion || '0',
                additional_named_addresses: additionalNamedAddresses,
            },
        })
            .toBytes());
        const compilerPayload = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawCompilerPayload = compilerPayload.deref();
        rawCompilerPayload.is_nil = false;
        rawCompilerPayload.len = compilerPayloadBytes.length;
        rawCompilerPayload.ptr = ref_napi_1.default.allocCString(compilerPayloadBytes.toString('base64'), 'base64');
        return rawCompilerPayload;
    };
    async getPackageName() {
        const moveTomlPath = path.join(this.packagePath, 'Move.toml');
        const moveToml = await (0, promises_1.readFile)(moveTomlPath).catch(() => {
            throw new Error(`Move.toml file not found at ${moveTomlPath}`);
        });
        const packageNameLine = new RegExp(/(name\s=\s"[0-9a-zA-Z_]+")/).exec(moveToml.toString('utf-8'));
        if (packageNameLine === null || packageNameLine.length === 0) {
            throw new Error('Move.toml is missing or has invalid "name" field format');
        }
        const packageName = new RegExp(/"[0-9a-zA-Z_]+"/).exec(packageNameLine[0]);
        if (packageName === null || packageName.length === 0) {
            throw new Error('Move.toml is missing or has invalid "name" field format');
        }
        return packageName[0].slice(1, -1);
    }
    async readFiles(subdir, extension) {
        const packageName = await this.getPackageName();
        const dirPath = path.join(this.packagePath, 'build', packageName, subdir);
        const files = await (0, promises_1.readdir)(dirPath).catch(() => {
            throw new Error(`Directory not found: ${dirPath}`);
        });
        const filteredFiles = files.filter((file) => file.endsWith(extension));
        if (filteredFiles.length === 0) {
            throw new Error(`No files found with extension "${extension}" in ${dirPath}`);
        }
        const results = await Promise.all(filteredFiles.map(async (file) => {
            const name = file.slice(0, -extension.length);
            const content = await (0, promises_1.readFile)(path.join(dirPath, file)).catch(() => {
                throw new Error(`Failed to read file: ${file}`);
            });
            return { name, content };
        }));
        return results.reduce((acc, { name, content }) => {
            acc[name] = content;
            return acc;
        }, {});
    }
    /**
     * Execute move compiler to generate new move package.
     * @param packageName - Name of the package.
     * @param moveVersion - Version of the move (default is 'main').
     * @param minitia - Boolean flag for minitia (default is false).
     * @returns If success, return "ok", else throw an error.
     */
    new(packageName, moveVersion = 'main', minitia = false) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const rawCompilerArgsPayload = this.makeRawBuildConfig();
        const packageNameView = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawPackageNameView = packageNameView.deref();
        rawPackageNameView.is_nil = false;
        rawPackageNameView.ptr = ref_napi_1.default.allocCString(packageName, 'utf-8');
        rawPackageNameView.len = Buffer.from(packageName, 'utf-8').length;
        const moveVersionView = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawMoveVersionView = moveVersionView.deref();
        rawMoveVersionView.is_nil = !moveVersion;
        rawMoveVersionView.ptr = moveVersion
            ? ref_napi_1.default.allocCString(moveVersion)
            : ref_napi_1.default.NULL;
        rawMoveVersionView.len = Buffer.from(moveVersion, 'utf-8').length;
        return (0, lib_1.handleResponse)(lib_1.libcompiler.create_new_move_package.async, errMsg, rawCompilerArgsPayload, rawPackageNameView, rawMoveVersionView, minitia);
    }
    /**
     * Execute move compiler to clean move package.
     * @param options - Options for cleaning the package.
     * @returns If success, return "ok", else throw an error.
     */
    async clean(options) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const rawCompilerArgsPayload = this.makeRawBuildConfig();
        return (0, lib_1.handleResponse)(lib_1.libcompiler.clean_move_package.async, errMsg, rawCompilerArgsPayload, options?.cleanCache || false, options?.cleanByProduct || false, true);
    }
    /**
     * Execute move compiler to generate move bytecode.
     * @returns If success, return "ok", else throw an error.
     */
    async build() {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const rawCompilerArgsPayload = this.makeRawBuildConfig();
        return (0, lib_1.handleResponse)(lib_1.libcompiler.build_move_package.async, errMsg, rawCompilerArgsPayload);
    }
    /**
     * Return compiled Move module bytecode.
     * @returns The module bytecode.
     */
    async getAll() {
        return this.readFiles('bytecode_modules', '.mv');
    }
    /**
     * Return compiled Move module bytecode.
     * @param moduleName - The module name to retrieve.
     * @returns The module bytecode.
     */
    async get(moduleName) {
        const bytecodeModules = await this.readFiles('bytecode_modules', '.mv');
        if (!bytecodeModules[moduleName]) {
            throw new Error(`Bytecode for module ${moduleName} not found`);
        }
        return bytecodeModules[moduleName];
    }
    /**
     * Return compiled Move module source map.
     * @returns The module source map.
     */
    async getAllSourceMaps() {
        return this.readFiles('source_maps', '.mvsm');
    }
    /**
     * Return compiled Move module source map.
     * @param moduleName - The module name to retrieve.
     * @returns The module source map.
     */
    async getSourceMap(moduleName) {
        const sourceMaps = await this.readFiles('source_maps', '.mvsm');
        if (!sourceMaps[moduleName]) {
            throw new Error(`Source map for module ${moduleName} not found`);
        }
        return sourceMaps[moduleName];
    }
    /**
     * Execute move compiler to unittest.
     * @param options - Move package test options.
     * @returns If success, return "ok", else throw an error.
     */
    async test(options) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const rawCompilerArgsPayload = this.makeRawBuildConfig();
        const testOptBytes = Buffer.from(types_1.testOptBcsType
            .serialize({
            filter: options?.filter || '',
            report_statistics: options?.reportStatistics || false,
            report_storage_on_error: options?.reportStorageOnError || false,
            ignore_compile_warnings: options?.ignoreCompileWarnings || false,
            compute_coverage: options?.computeCoverage || false,
        })
            .toBytes());
        const testOpt = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawTestOpt = testOpt.deref();
        rawTestOpt.is_nil = false;
        rawTestOpt.len = testOptBytes.length;
        rawTestOpt.ptr = ref_napi_1.default.allocCString(testOptBytes.toString('base64'), 'base64');
        return (0, lib_1.handleResponse)(lib_1.libcompiler.test_move_package.async, errMsg, rawCompilerArgsPayload, rawTestOpt);
    }
    /**
     * Decode module bytes to move module.
     * @param moduleBytes - Move module bytes.
     * @returns If success, return buffer, else throw an error.
     */
    static async decodeModuleBytes(moduleBytes) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const moduleBytesView = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawModuleBytesView = moduleBytesView.deref();
        rawModuleBytesView.is_nil = false;
        rawModuleBytesView.ptr = ref_napi_1.default.allocCString(moduleBytes.toString('base64'), 'base64');
        rawModuleBytesView.len = moduleBytes.length;
        const response = await (0, lib_1.handleResponse)(lib_1.libmovevm.decode_module_bytes.async, errMsg, rawModuleBytesView);
        if (response === null) {
            throw new Error('Failed to decode module bytes');
        }
        return JSON.parse(response.toString());
    }
    /**
     * Decode script bytes to move function.
     * @param scriptBytes - Move script bytes.
     * @returns If success, return buffer, else throw an error.
     */
    static async decodeScriptBytes(scriptBytes) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const scriptBytesView = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawScriptBytesView = scriptBytesView.deref();
        rawScriptBytesView.is_nil = false;
        rawScriptBytesView.ptr = ref_napi_1.default.allocCString(scriptBytes.toString('base64'), 'base64');
        rawScriptBytesView.len = scriptBytes.length;
        const response = await (0, lib_1.handleResponse)(lib_1.libmovevm.decode_script_bytes.async, errMsg, rawScriptBytesView);
        if (response === null) {
            throw new Error('Failed to decode module bytes');
        }
        return JSON.parse(response.toString());
    }
    /**
     * Read module info from bytes.
     * @param compiledBinary - Move compiled bytes.
     * @returns If success, return buffer, else throw an error.
     */
    static async readModuleInfo(compiledBinary) {
        const errMsg = (0, lib_1.createRawErrMsg)();
        const compiledView = ref_napi_1.default.alloc(types_1.ByteSliceViewType);
        const rawCompiledView = compiledView.deref();
        rawCompiledView.is_nil = false;
        rawCompiledView.ptr = ref_napi_1.default.allocCString(compiledBinary.toString('base64'), 'base64');
        rawCompiledView.len = compiledBinary.length;
        const response = await (0, lib_1.handleResponse)(lib_1.libmovevm.read_module_info.async, errMsg, rawCompiledView);
        if (response === null) {
            throw new Error('Failed to decode module bytes');
        }
        const res = JSON.parse(response.toString());
        return {
            address: '0x' + Buffer.from(res.address).toString('hex').replace(/^0+/, ''),
            name: res.name,
        };
    }
}
exports.MoveBuilder = MoveBuilder;
//# sourceMappingURL=builder.js.map