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 __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
    var g = generator.apply(thisArg, _arguments || []), i, q = [];
    return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
    function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
    function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
    function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
    function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
    function fulfill(value) { resume("next", value); }
    function reject(value) { resume("throw", value); }
    function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
import * as fs from "fs";
import * as path from "path";
import { defined, exceptionToString } from "@glideapps/ts-necessities";
import { isNode } from "browser-or-node";
import isURL from "is-url";
import { Readable } from "readable-stream";
import { messageError } from "../../Messages.js";
import { panic } from "../../support/Support.js";
import { filePathFromFileURI } from "../../support/WindowsPaths.js";
import { getStream } from "./get-stream/index.js";
function parseHeaders(httpHeaders) {
    if (!Array.isArray(httpHeaders)) {
        return {};
    }
    return httpHeaders.reduce((result, httpHeader) => {
        if (httpHeader !== undefined && httpHeader.length > 0) {
            const split = httpHeader.indexOf(":");
            if (split < 0) {
                return panic(`Could not parse HTTP header "${httpHeader}".`);
            }
            const key = httpHeader.slice(0, split).trim();
            const value = httpHeader.slice(split + 1).trim();
            result[key] = value;
        }
        return result;
    }, {});
}
function webStreamChunks(stream) {
    return __asyncGenerator(this, arguments, function* webStreamChunks_1() {
        const reader = stream.getReader();
        try {
            for (;;) {
                const { done, value } = yield __await(reader.read());
                if (done)
                    return yield __await(void 0);
                yield yield __await(defined(value));
            }
        }
        finally {
            reader.releaseLock();
        }
    });
}
// readable-stream implements Readable.from (it can't do Readable.fromWeb),
// but @types/readable-stream doesn't declare it, hence this cast.
const ReadableWithFrom = Readable;
function readableFromResponseBody(body) {
    // Native fetch returns a WHATWG ReadableStream, which lacks the Node
    // stream API that our consumers rely on, so we have to wrap it.
    if (typeof body.getReader === "function") {
        return ReadableWithFrom.from(webStreamChunks(body), { objectMode: false });
    }
    return body;
}
function resolveSymbolicLink(filePath) {
    if (!fs.lstatSync(filePath).isSymbolicLink()) {
        return filePath;
    }
    const linkPath = fs.readlinkSync(filePath);
    if (path.isAbsolute(linkPath)) {
        return linkPath;
    }
    return path.join(path.dirname(filePath), linkPath);
}
export function readableFromFileOrURL(fileOrURL, httpHeaders) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            if (fileOrURL.startsWith("file://")) {
                fileOrURL = filePathFromFileURI(fileOrURL);
            }
            else if (isURL(fileOrURL)) {
                const response = yield globalThis.fetch(fileOrURL, {
                    headers: parseHeaders(httpHeaders),
                });
                return readableFromResponseBody(defined(response.body));
            }
            if (isNode) {
                if (fileOrURL === "-") {
                    // Cast node readable to isomorphic readable from readable-stream
                    return process.stdin;
                }
                const filePath = resolveSymbolicLink(fileOrURL);
                if (fs.existsSync(filePath)) {
                    // Cast node readable to isomorphic readable from readable-stream
                    return fs.createReadStream(filePath, "utf8");
                }
            }
        }
        catch (e) {
            return messageError("MiscReadError", {
                fileOrURL,
                message: exceptionToString(e),
            });
        }
        return messageError("DriverInputFileDoesNotExist", { filename: fileOrURL });
    });
}
export function readFromFileOrURL(fileOrURL, httpHeaders) {
    return __awaiter(this, void 0, void 0, function* () {
        const readable = yield readableFromFileOrURL(fileOrURL, httpHeaders);
        try {
            return yield getStream(readable);
        }
        catch (e) {
            return messageError("MiscReadError", {
                fileOrURL,
                message: exceptionToString(e),
            });
        }
    });
}
