// @ts-check
const stream = require("stream");
const Vinyl = require("vinyl");
const ts = require("../../lib/typescript");
const fs = require("fs");
const { base64VLQFormatEncode } = require("./sourcemaps");

/**
 * @param {string | ((file: import("vinyl")) => string)} data
 */
function prepend(data) {
    return new stream.Transform({
        objectMode: true,
        /**
         * @param {string | Buffer | import("vinyl")} input
         * @param {(error: Error, data?: any) => void} cb
         */
        transform(input, _, cb) {
            if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported."));
            if (!input.isBuffer()) return cb(new Error("Streams not supported."));
            try {
                const output = input.clone();
                const prependContent = typeof data === "function" ? data(input) : data;
                output.contents = Buffer.concat([Buffer.from(prependContent, "utf8"), input.contents]);
                if (input.sourceMap) {
                    if (typeof input.sourceMap === "string") input.sourceMap = /**@type {import("./sourcemaps").RawSourceMap}*/(JSON.parse(input.sourceMap));
                    const lineStarts = /**@type {*}*/(ts).computeLineStarts(prependContent);
                    let prependMappings = "";
                    for (let i = 1; i < lineStarts.length; i++) {
                        prependMappings += ";";
                    }
                    const offset = prependContent.length - lineStarts[lineStarts.length - 1];
                    if (offset > 0) {
                        prependMappings += base64VLQFormatEncode(offset) + ",";
                    }
                    output.sourceMap = {
                        version: input.sourceMap.version,
                        file: input.sourceMap.file,
                        sources: input.sourceMap.sources,
                        sourceRoot: input.sourceMap.sourceRoot,
                        mappings: prependMappings + input.sourceMap.mappings,
                        names: input.names,
                        sourcesContent: input.sourcesContent
                    };
                }
                return cb(null, output);
            }
            catch (e) {
                return cb(e);
            }
        }
    })
}
exports.prepend = prepend;

/**
 * @param {string | ((file: import("vinyl")) => string)} file
 */
function prependFile(file) {
    const data = typeof file === "string" ? fs.readFileSync(file, "utf8") :
        vinyl => fs.readFileSync(file(vinyl), "utf8");
    return prepend(data)
}
exports.prependFile = prependFile;