import { Command } from 'commander';
import { canonicalEncode, KeyedDictionary, underlying } from '@preserves/core';
import fs from 'fs';
import path from 'path';
import * as M from '../meta';
import { expandInputGlob, formatFailures } from './cli-utils';

export type CommandLineArguments = {
    inputs: string[];
    bundle: boolean;
};

export function run(options: CommandLineArguments): void {
    const { failures, inputFiles } = expandInputGlob(options.inputs, []);

    if (!options.bundle && inputFiles.length !== 1) {
        failures.push({ type: 'error', file: null, detail: {
            message: 'Cannot emit non-bundle with anything other than exactly one input file',
            pos: null,
        }});
    }

    formatFailures(failures);

    if (failures.length === 0) {
        if (options.bundle) {
            fs.writeSync(1, underlying(canonicalEncode(M.fromBundle({
                modules: new KeyedDictionary<M.ModulePath, M.Schema, M.InputEmbedded>(
                    inputFiles.map(i => [i.modulePath, i.schema])),
            }))));
        } else {
            fs.writeSync(1, underlying(canonicalEncode(M.fromSchema(inputFiles[0].schema))));
        }
    } else {
        process.exit(1);
    }
}

export function main(argv: Array<string>) {
    new Command()
        .arguments('[input...]')
        .description('Compile textual Preserves schema definitions to binary format', {
            input: 'Input directory, with optional ":glob" appended (defaults to ":**/*.prs")',
        })
        .option('--no-bundle', 'Emit a single Schema instead of a schema Bundle')
        .action((inputs: string[], rawOptions) => {
            const options: CommandLineArguments = {
                inputs: inputs.map(i => path.normalize(i)),
                bundle: rawOptions.bundle,
            };
            Error.stackTraceLimit = Infinity;
            run(options);
        })
        .parse(argv, { from: 'user' });
}
