"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCoinBuildOutputManual = void 0;
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const MoveToml_1 = require("./MoveToml");
const utils_1 = require("./utils");
// TODO(aki): Remove this when this branch is merged in
const TEMPORARY_SUI_BRANCH = "sui/integration_v2";
const getCoinBuildOutputManual = (network, coreBridgePackageId, tokenBridgePackageId, vaa) => __awaiter(void 0, void 0, void 0, function* () {
    yield cloneDependencies();
    setupMainToml(`${__dirname}/dependencies/wormhole`, network, coreBridgePackageId);
    setupMainToml(`${__dirname}/dependencies/token_bridge`, network, tokenBridgePackageId);
    setupCoin(coreBridgePackageId, tokenBridgePackageId, vaa);
    const buildOutput = buildPackage(`${__dirname}/wrapped_coin`);
    cleanupTempToml(`${__dirname}/dependencies/wormhole`);
    cleanupTempToml(`${__dirname}/dependencies/token_bridge`);
    return buildOutput;
});
exports.getCoinBuildOutputManual = getCoinBuildOutputManual;
const buildPackage = (packagePath) => {
    return JSON.parse(child_process_1.execSync(`sui move build --dump-bytecode-as-base64 --path ${packagePath} 2> /dev/null`, {
        encoding: "utf-8",
    }));
};
const cleanupTempToml = (packagePath) => {
    const defaultTomlPath = getDefaultTomlPath(packagePath);
    const tempTomlPath = getTempTomlPath(packagePath);
    if (fs_1.default.existsSync(tempTomlPath)) {
        fs_1.default.renameSync(tempTomlPath, defaultTomlPath);
    }
};
const cloneDependencies = () => __awaiter(void 0, void 0, void 0, function* () {
    var _a;
    const { tree, sha: latestSha } = yield fetchWormholeTree(TEMPORARY_SUI_BRANCH);
    const suiSha = (_a = tree.find((n) => n.path === "sui")) === null || _a === void 0 ? void 0 : _a.sha;
    if (!suiSha) {
        throw new Error("Failed to fetch url");
    }
    const promises = [];
    const { tree: suiTree } = yield fetchWormholeTree(suiSha, true);
    promises.push(fetchWormholeFilesFromTree(suiTree, latestSha, "wormhole"));
    promises.push(fetchWormholeFilesFromTree(suiTree, latestSha, "token_bridge"));
    yield Promise.all(promises);
});
const fetchWormholeFile = (sha, path) => __awaiter(void 0, void 0, void 0, function* () {
    const res = yield fetch(`https://raw.githubusercontent.com/wormhole-foundation/wormhole/${sha}/sui/${path}`);
    return res.text();
});
// TODO(aki): we can further optimize subsequent runs by caching every dir/blob
const fetchWormholeFilesFromTree = (tree, sha, subdirName) => __awaiter(void 0, void 0, void 0, function* () {
    var _b;
    if (!tree || !tree.length) {
        throw new Error("Received empty tree");
    }
    const latestSha = (_b = tree.find((n) => n.path === subdirName)) === null || _b === void 0 ? void 0 : _b.sha;
    if (!latestSha) {
        throw new Error(`Invalid response, couldn't find node with path ${subdirName}`);
    }
    const shaPath = `${__dirname}/dependencies/${subdirName}/.sha`;
    if (fs_1.default.existsSync(shaPath) &&
        fs_1.default.readFileSync(shaPath, "utf-8") === latestSha) {
        return;
    }
    else {
        fs_1.default.rmSync(`${__dirname}/dependencies/${subdirName}`, {
            recursive: true,
            force: true,
        });
    }
    const wormhole = tree.filter((n) => n.path.startsWith(subdirName) && n.type === "blob");
    const promises = [];
    for (const node of wormhole) {
        const localPath = `${__dirname}/dependencies/${node.path}`;
        promises.push(fs_1.default.promises
            .mkdir(path_1.default.dirname(localPath), { recursive: true })
            .then(() => fetchWormholeFile(sha, node.path))
            .then((file) => fs_1.default.promises.writeFile(localPath, file)));
    }
    yield Promise.all(promises);
    fs_1.default.writeFileSync(shaPath, latestSha);
});
const fetchWormholeTree = (sha, recursive = false) => __awaiter(void 0, void 0, void 0, function* () {
    const res = yield fetch(`https://api.github.com/repos/wormhole-foundation/wormhole/git/trees/${sha}?recursive=${recursive}`);
    return res.json();
});
const getDefaultTomlPath = (packagePath) => `${packagePath}/Move.toml`;
const getTempTomlPath = (packagePath) => `${packagePath}/Move.temp.toml`;
const getTomlPathByNetwork = (packagePath, network) => `${packagePath}/Move.${network.toLowerCase()}.toml`;
const getPackageNameFromPath = (packagePath) => packagePath.split("/").pop() || "";
// TODO(aki): parallelize
const setupCoin = (coreBridgePackageId, tokenBridgePackageId, vaa) => {
    fs_1.default.rmSync(`${__dirname}/wrapped_coin`, { recursive: true, force: true });
    fs_1.default.mkdirSync(`${__dirname}/wrapped_coin/sources`, { recursive: true });
    const coin = fs_1.default
        .readFileSync(`${__dirname}/templates/wrapped_coin/coin.move`, "utf8")
        .toString();
    fs_1.default.writeFileSync(`${__dirname}/wrapped_coin/sources/coin.move`, coin.replace(`{{VAA_BYTES}}`, vaa), "utf8");
    const toml = new MoveToml_1.MoveToml(`${__dirname}/templates/wrapped_coin/Move.toml`)
        .updateRow("addresses", "wormhole", coreBridgePackageId)
        .updateRow("addresses", "token_bridge", tokenBridgePackageId)
        .serialize();
    fs_1.default.writeFileSync(`${__dirname}/wrapped_coin/Move.toml`, toml, "utf8");
};
const setupMainToml = (packagePath, network, publishedAddress, isDependency = false) => {
    if (!utils_1.isValidSuiAddress(publishedAddress)) {
        throw new Error(`Invalid address ${publishedAddress} for package ${packagePath}`);
    }
    const defaultTomlPath = getDefaultTomlPath(packagePath);
    const tempTomlPath = getTempTomlPath(packagePath);
    const networkTomlPath = getTomlPathByNetwork(packagePath, network);
    if (fs_1.default.existsSync(tempTomlPath)) {
        // It's possible that this dependency has been set up by another package
        if (isDependency) {
            return;
        }
        cleanupTempToml(packagePath);
    }
    // Save default Move.toml
    if (!fs_1.default.existsSync(defaultTomlPath)) {
        throw new Error(`Invalid package layout. Move.toml not found at ${defaultTomlPath}`);
    }
    fs_1.default.renameSync(defaultTomlPath, tempTomlPath);
    // Set Move.toml from appropriate network
    if (!fs_1.default.existsSync(networkTomlPath)) {
        throw new Error(`Move.toml for ${network} not found at ${networkTomlPath}`);
    }
    fs_1.default.copyFileSync(networkTomlPath, defaultTomlPath);
    // Replace undefined addresses in base Move.toml and ensure dependencies are
    // published
    const tomlStr = fs_1.default.readFileSync(defaultTomlPath, "utf8").toString();
    const toml = new MoveToml_1.MoveToml(tomlStr);
    const packageName = getPackageNameFromPath(packagePath);
    if (!isDependency) {
        if (toml.isPublished()) {
            throw new Error(`Package ${packageName} is already published.`);
        }
        fs_1.default.writeFileSync(defaultTomlPath, toml
            .addRow("package", "published-at", publishedAddress)
            .updateRow("addresses", packageName, publishedAddress)
            .serialize());
    }
};
