var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _Headers_headers, _Request_options, _Request_url, _Request_bodyUsed, _Response_instances, _Response_pluginResponse, _Response_headers, _Response_bodyUsed, _Response_resultMethodUnimplemented;
import { HTTPDefinition, } from '@devvit/protos/types/devvit/plugin/http/http.js';
import { Buffer } from 'buffer';
let httpPlugin;
function getHttpPlugin() {
    if (httpPlugin !== undefined) {
        return httpPlugin;
    }
    const config = globalThis?.devvit?.config;
    if (!config?.uses?.(HTTPDefinition)) {
        throw new Error('Fetch is not enabled. You can enable it by passing `http: true` to `Devvit.configure`');
    }
    const handler = config?.use?.(HTTPDefinition, {}) ?? null;
    if (handler === null) {
        throw new Error('fetch polyfill failed to initialize HTTPPlugin');
    }
    return handler;
}
export class Headers {
    constructor(init) {
        _Headers_headers.set(this, new Map());
        if (init instanceof Headers) {
            __classPrivateFieldSet(this, _Headers_headers, new Map(Object.entries(__classPrivateFieldGet(init, _Headers_headers, "f"))), "f");
        }
        else if (typeof init === 'object') {
            const h = Array.isArray(init) ? init : Object.entries(init);
            for (const [name, val] of h) {
                this.set(name, val);
            }
        }
    }
    entries() {
        return __classPrivateFieldGet(this, _Headers_headers, "f").entries();
    }
    append(name, val) {
        const current = this.get(name) ?? '';
        this.set(name, `${current},${val}`);
    }
    set(name, val) {
        __classPrivateFieldGet(this, _Headers_headers, "f").set(name.toLowerCase(), val);
    }
    get(name) {
        return __classPrivateFieldGet(this, _Headers_headers, "f").get(name.toLowerCase()) ?? null;
    }
    delete(name) {
        __classPrivateFieldGet(this, _Headers_headers, "f").delete(name.toLowerCase());
    }
    has(name) {
        return this.get(name) !== null;
    }
    forEach(cb, thisArg) {
        __classPrivateFieldGet(this, _Headers_headers, "f").forEach((val, name) => {
            cb.call(thisArg, val, name, this);
        });
    }
    keys() {
        return __classPrivateFieldGet(this, _Headers_headers, "f").keys();
    }
    values() {
        return __classPrivateFieldGet(this, _Headers_headers, "f").values();
    }
}
_Headers_headers = new WeakMap();
class Request {
    constructor(input, options) {
        _Request_options.set(this, void 0);
        _Request_url.set(this, void 0);
        _Request_bodyUsed.set(this, false);
        if (typeof input === 'string') {
            __classPrivateFieldSet(this, _Request_url, input, "f");
            __classPrivateFieldSet(this, _Request_options, options ?? {}, "f");
        }
        else {
            __classPrivateFieldSet(this, _Request_url, __classPrivateFieldGet(input, _Request_url, "f"), "f");
            __classPrivateFieldSet(this, _Request_options, { ...__classPrivateFieldGet(input, _Request_options, "f"), ...(options ?? {}) }, "f");
        }
        this.headers = new Headers(__classPrivateFieldGet(this, _Request_options, "f").headers);
    }
    get url() {
        return __classPrivateFieldGet(this, _Request_url, "f");
    }
    get method() {
        return __classPrivateFieldGet(this, _Request_options, "f").method ?? 'GET';
    }
    // TODO: make this return ReadableStream, but that's a lot more to polyfill...
    get body() {
        throw new Error('Request.body is not implemented');
    }
    // NON-STANDARD way of getting the body as an ArrayBuffer to Avoid ReadableStreams
    getBodyAsArrayBuffer() {
        __classPrivateFieldSet(this, _Request_bodyUsed, true, "f");
        const body = __classPrivateFieldGet(this, _Request_options, "f").body;
        if (typeof body === 'string') {
            return new TextEncoder().encode(body);
        }
        else if (body instanceof ArrayBuffer) {
            return body;
        }
        else if (typeof body === 'object' && 'buffer' in body && body.buffer instanceof ArrayBuffer) {
            return body.buffer;
        }
        else if (typeof body === 'undefined') {
            return new ArrayBuffer(0);
        }
        else {
            throw new Error('Request.body must be a TypedArray, DataView, ArrayBuffer, string, or undefined');
        }
    }
    get bodyUsed() {
        return __classPrivateFieldGet(this, _Request_bodyUsed, "f");
    }
    get mode() {
        return 'cors';
    }
    get cache() {
        return 'no-cache';
    }
    get credentials() {
        // we don't implement cookies, http basic auth, etc
        return 'omit';
    }
    get destination() {
        // this is the default
        return '';
    }
    get integrity() {
        // this is the default
        return '';
    }
    // this is expermiental
    get priority() {
        return 'auto';
    }
    get redirect() {
        // redirects should be handled in the plugin
        return 'follow';
    }
    get referrer() {
        // empty string means no referrer
        return '';
    }
    get referrerPolicy() {
        return '';
    }
}
_Request_options = new WeakMap(), _Request_url = new WeakMap(), _Request_bodyUsed = new WeakMap();
class Response {
    constructor(unsupportedFileOrBlob) {
        _Response_instances.add(this);
        _Response_pluginResponse.set(this, void 0);
        _Response_headers.set(this, void 0);
        _Response_bodyUsed.set(this, false);
        if (unsupportedFileOrBlob !== undefined) {
            throw new Error('Response does not support construction from File or Blob');
        }
    }
    clone() {
        if (__classPrivateFieldGet(this, _Response_pluginResponse, "f") == null) {
            return new Response();
        }
        return Response.fromPluginResponse(__classPrivateFieldGet(this, _Response_pluginResponse, "f"));
    }
    async arrayBuffer() {
        // return an empty ArrayBuffer if the plugin response was never set
        if (__classPrivateFieldGet(this, _Response_pluginResponse, "f") == null) {
            return new ArrayBuffer(0);
        }
        return Buffer.from(__classPrivateFieldGet(this, _Response_pluginResponse, "f").body);
    }
    async text() {
        __classPrivateFieldSet(this, _Response_bodyUsed, true, "f");
        const body = await this.arrayBuffer();
        // TODO: can we actually do the text decoding in the background like
        // the spec (and the returned Promise) imply?
        return new TextDecoder().decode(body);
    }
    async json() {
        const text = await this.text();
        return JSON.parse(text);
    }
    get headers() {
        if (__classPrivateFieldGet(this, _Response_headers, "f") != null) {
            return __classPrivateFieldGet(this, _Response_headers, "f");
        }
        __classPrivateFieldSet(this, _Response_headers, new Headers(__classPrivateFieldGet(this, _Response_pluginResponse, "f")?.headers), "f");
        return __classPrivateFieldGet(this, _Response_headers, "f");
    }
    get bodyUsed() {
        return __classPrivateFieldGet(this, _Response_bodyUsed, "f");
    }
    get status() {
        return __classPrivateFieldGet(this, _Response_pluginResponse, "f")?.status ?? 200;
    }
    get statusText() {
        // TODO: implement this from the table of response codes here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
        throw new Error('Response.statusText() is not implemented, please use Response.status()');
    }
    get ok() {
        return 200 <= this.status && this.status < 300;
    }
    get redirected() {
        // TODO: should this be implemented? Should apps care since we follow redirects for them?
        return false;
    }
    get type() {
        // TODO: verify this is always the request type
        return 'cors';
    }
    get url() {
        return __classPrivateFieldGet(this, _Response_pluginResponse, "f")?.url ?? '';
    }
    get body() {
        return __classPrivateFieldGet(this, _Response_instances, "m", _Response_resultMethodUnimplemented).call(this, 'body');
    }
    get blob() {
        return __classPrivateFieldGet(this, _Response_instances, "m", _Response_resultMethodUnimplemented).call(this, 'blob()');
    }
    formData() {
        __classPrivateFieldGet(this, _Response_instances, "m", _Response_resultMethodUnimplemented).call(this, 'formData()');
    }
    static error() {
        return Response.fromPluginResponse({
            url: '',
            body: new Uint8Array(0),
            status: 0,
            headers: {},
        });
    }
    static redirect(url, status = 0) {
        return Response.fromPluginResponse({
            url,
            status,
            body: new Uint8Array(0),
            headers: {},
        });
    }
    /**
     * This method allows us to construct a Response from a PluginResponse,
     * but this should not be considered part of the public api
     */
    static fromPluginResponse(pr) {
        const resp = new Response();
        __classPrivateFieldSet(resp, _Response_pluginResponse, pr, "f");
        return resp;
    }
}
_Response_pluginResponse = new WeakMap(), _Response_headers = new WeakMap(), _Response_bodyUsed = new WeakMap(), _Response_instances = new WeakSet(), _Response_resultMethodUnimplemented = function _Response_resultMethodUnimplemented(method) {
    throw new Error(`Response.${method} is not implemented! Please use Response.text() or Response.json().`);
};
async function fetch(request, options) {
    const fetchPlugin = getHttpPlugin();
    if (typeof request === 'string' || options !== undefined) {
        request = new Request(request, options);
    }
    const pluginResponse = await fetchPlugin.Fetch({
        url: request.url,
        data: {
            method: request.method,
            headers: Object.fromEntries([...request.headers.entries()]),
            body: new Uint8Array(request.getBodyAsArrayBuffer()),
        },
    });
    return Response.fromPluginResponse(pluginResponse);
}
const globalExports = {
    fetch,
    Request,
    Response,
    Headers,
};
const global = globalThis;
Object.assign(global, globalExports);
