#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const package_json_1 = __importDefault(require("../package.json"));
const commander_1 = require("commander");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const xbytes_1 = __importDefault(require("xbytes"));
const DealPreparationService_1 = __importDefault(require("./deal-preparation/DealPreparationService"));
const Scanner_1 = __importDefault(require("./deal-preparation/Scanner"));
const DealPreparationWorker_1 = __importDefault(require("./deal-preparation/DealPreparationWorker"));
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const task_queue_1 = __importDefault(require("@goodware/task-queue"));
const crypto_1 = require("crypto");
const version = package_json_1.default.version;
const program = new commander_1.Command();
program.name('singularity-prepare')
    .version(version)
    .description('A tool to prepare dataset for slingshot')
    .argument('<datasetName>', 'Name of the dataset')
    .argument('<datasetPath>', 'Directory path to the dataset')
    .argument('<outDir>', 'The output Directory to save CAR files and manifest files')
    .requiredOption('-l, --url-prefix <urlPrefix>', 'The prefix of the download link, which will be followed by datacid.car, i.e. http://download.mysite.org/')
    .option('-s, --deal-size <deal_size>', 'Target deal size, i.e. 32GiB', '32 GiB')
    .option('-t, --tmp-dir <tmp_dir>', 'Temporary directory, may be useful when dataset source is slow, such as on S3 mount or NFS')
    .addOption(new commander_1.Option('-m, --min-ratio <min_ratio>', 'Min ratio of deal to sector size, i.e. 0.55').default('0.55').argParser(parseFloat))
    .addOption(new commander_1.Option('-M, --max-ratio <max_ratio>', 'Max ratio of deal to sector size, i.e. 0.95').default('0.95').argParser(parseFloat))
    .addOption(new commander_1.Option('-j, --parallel <parallel>', 'How many generation jobs to run at the same time').default('1'))
    .action(async (name, p, outDir, options) => {
    await fs_extra_1.default.mkdir(outDir, { recursive: true });
    if (!await fs_extra_1.default.pathExists(p)) {
        console.error(`Dataset path "${p}" does not exist.`);
        process.exit(1);
    }
    p = path_1.default.resolve(p);
    outDir = path_1.default.resolve(outDir);
    const dealSize = options.dealSize;
    const minRatio = options.minRatio;
    const maxRatio = options.maxRatio;
    const dealSizeNumber = xbytes_1.default.parseSize(dealSize);
    // Validate dealSize
    if (!DealPreparationService_1.default.AllowedDealSizes.includes(dealSizeNumber)) {
        console.error(`Deal Size ${dealSize} is not valid`);
        process.exit(1);
    }
    if (minRatio && (minRatio < 0.5 || minRatio > 0.95)) {
        console.error(`minRatio Size ${minRatio} is not valid`);
        process.exit(1);
    }
    if (maxRatio && (maxRatio < 0.5 || maxRatio > 0.95)) {
        console.error(`maxRatio Size ${maxRatio} is not valid`);
        process.exit(1);
    }
    if (minRatio && maxRatio && minRatio >= maxRatio) {
        console.error(`maxRatio Size ${maxRatio} is not valid`);
        process.exit(1);
    }
    const minSize = Math.round(minRatio * dealSizeNumber);
    const maxSize = Math.round(maxRatio * dealSizeNumber);
    console.error(`Generating with minSize ${minSize}, maxSize ${maxSize}`);
    if (!options.urlPrefix.endsWith('/')) {
        options.urlPrefix = options.urlPrefix + '/';
    }
    const queue = new task_queue_1.default({ workers: parseInt(options.parallel) });
    let task;
    for await (const fileList of Scanner_1.default.scan(p, minSize, maxSize)) {
        console.log('Pushed a new generation request');
        task = await queue.push(async () => {
            const input = JSON.stringify(fileList.map(file => ({
                Path: file.path,
                Size: file.size,
                Start: file.start,
                End: file.end
            })));
            let tmpDir;
            if (options.tmpDir) {
                tmpDir = path_1.default.join(path_1.default.resolve(options.tmpDir), (0, crypto_1.randomUUID)());
            }
            const [stdout, stderr, exitCode] = await DealPreparationWorker_1.default.invokeGenerateCar(input, outDir, p, tmpDir);
            if (tmpDir) {
                await fs_extra_1.default.rm(tmpDir, { recursive: true, force: true });
            }
            if (exitCode !== 0) {
                console.error(stderr);
            }
            const output = JSON.parse(stdout);
            const carFile = path_1.default.join(outDir, output.DataCid + '.car');
            console.log(`Generated a new car ${carFile}`);
            const carFileStat = await fs_extra_1.default.stat(carFile);
            const fileMap = new Map();
            for (const fileInfo of fileList) {
                fileMap.set(path_1.default.relative(p, fileInfo.path), fileInfo);
            }
            const generatedFileList = [];
            await DealPreparationWorker_1.default.populateGeneratedFileList(fileMap, output.Ipld, [], [], generatedFileList);
            const [contents, groupings] = DealPreparationService_1.default.getContentsAndGroupings(generatedFileList);
            const result = {
                piece_cid: output.PieceCid,
                payload_cid: output.DataCid,
                raw_car_file_size: carFileStat.size,
                car_file_link: options.urlPrefix + output.DataCid + '.car',
                dataset: name,
                contents,
                groupings
            };
            await fs_extra_1.default.writeJSON(path_1.default.join(outDir, `${output.DataCid}.manifest`), result);
        });
    }
    if (task) {
        await task.promise;
    }
});
program.parse();
//# sourceMappingURL=singularity-prepare.js.map