import { buildInfo } from './BuildInfo';
/**
 * HTTP Status codes (only a subset is used in the current model)
 */
export var HttpStatusCode;
(function (HttpStatusCode) {
    HttpStatusCode[HttpStatusCode["Ok"] = 200] = "Ok";
    HttpStatusCode[HttpStatusCode["Created"] = 201] = "Created";
    HttpStatusCode[HttpStatusCode["Accepted"] = 202] = "Accepted";
    HttpStatusCode[HttpStatusCode["NoContent"] = 204] = "NoContent";
    HttpStatusCode[HttpStatusCode["Aborted"] = 299] = "Aborted";
    HttpStatusCode[HttpStatusCode["Unauthorized"] = 401] = "Unauthorized";
    HttpStatusCode[HttpStatusCode["Forbidden"] = 403] = "Forbidden";
    HttpStatusCode[HttpStatusCode["Conflict"] = 409] = "Conflict";
    HttpStatusCode[HttpStatusCode["NotFound"] = 404] = "NotFound";
    HttpStatusCode[HttpStatusCode["InternalServerError"] = 500] = "InternalServerError";
})(HttpStatusCode || (HttpStatusCode = {}));
/** Creates a mocked TypedResponse for testing */
export function createMockTypedResponse(o) {
    return {
        ok: o.ok ?? true,
        redirected: o.redirected ?? false,
        status: o.status ?? 200,
        statusText: o.statusText ?? 'OK',
        bytes: o.bytes ?? (() => Promise.resolve(new Uint8Array())),
        headers: o.headers ?? new Headers(),
        body: o.body ?? null,
        bodyUsed: o.bodyUsed ?? false,
        //trailer: o.trailer ?? Promise.resolve(new Headers()),
        arrayBuffer: o.arrayBuffer ?? notImplemented,
        blob: o.blob ?? notImplemented,
        text: o.text ?? notImplemented,
        formData: o.formData ?? notImplemented,
        json: o.json ?? notImplemented,
        clone: o.clone ?? notImplemented,
        type: o.type ?? 'default',
        url: o.url ?? 'http://foo.com',
        value: Promise.resolve(o.value)
    };
    function notImplemented() {
        throw new Error('');
    }
}
/**
 * HTTP methods
 */
export var HttpMethod;
(function (HttpMethod) {
    HttpMethod["Post"] = "POST";
    HttpMethod["Put"] = "PUT";
    HttpMethod["Delete"] = "DELETE";
    HttpMethod["Get"] = "GET";
    HttpMethod["Patch"] = "PATCH";
})(HttpMethod || (HttpMethod = {}));
/**
 * Utility class for making HTTP requests. If requests are made to the Twinfinity
 * backend then correct headers are automatically set.
 */
export class Http {
    static _authorizationHeaderProvider;
    /**
     * Issues a GET HTTP request
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async get(absoluteUrl, responseconverter, init) {
        return Http.fetch(HttpMethod.Get, absoluteUrl, responseconverter, init);
    }
    /**
     * Issues a POST HTTP request
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async post(absoluteUrl, responseconverter, init) {
        return Http.fetch(HttpMethod.Post, absoluteUrl, responseconverter, init);
    }
    /**
     * Issues a DELETE HTTP request
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async delete(absoluteUrl, responseconverter, init) {
        return Http.fetch(HttpMethod.Delete, absoluteUrl, responseconverter, init);
    }
    /**
     * Issues a PUT HTTP request
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async put(absoluteUrl, responseconverter, init) {
        return Http.fetch(HttpMethod.Put, absoluteUrl, responseconverter, init);
    }
    /**
     * Issues a PATCH HTTP request
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async patch(absoluteUrl, responseconverter, init) {
        return Http.fetch(HttpMethod.Patch, absoluteUrl, responseconverter, init);
    }
    /**
     * Issues a HTTP request
     * @param method Method of http request.
     * @param absoluteUrl Absolute url
     * @param responseconverter How response should be converted. Use functions on {@link HttpResponseType}
     * @param init Optional {@link HttpRequestInit} values.
     */
    static async fetch(method, absoluteUrl, responseconverter, init) {
        absoluteUrl = Http.toURL(absoluteUrl);
        const tmpInit = { method };
        Object.assign(tmpInit, init ?? {});
        if (Http.isTwinfinityUrl(absoluteUrl)) {
            tmpInit.credentials = 'include';
            // These headers prevent so called simple CORS requests (for example GET) to twinfinity backend. That is requests
            // that are not preflighted (with OPTIONS request).  See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
            // Server does set Access-Control-Max-Age so browser can cache result of preflight requests though
            // (so it does not always have to issue them if a cached result already exist). For now it looks like that is enough. However
            // if it turns out that making simple requests will increase performance/reduce backend load then it might
            // be a good idea to revisit this and disable the custom headers (for simple requests). They are nice to have though
            // since backend can look at them and do stuff like transform data to keep compability with old client versions
            // if neeeded etc.
            tmpInit.headers = new Headers(init?.headers ?? {});
            tmpInit.headers.set('twinfinity-client-version', buildInfo.version);
        }
        if (Http.requiresAuthorizationHeader(absoluteUrl)) {
            tmpInit.headers = new Headers(init?.headers ?? {});
            const authorizationHeader = await this._authorizationHeaderProvider?.getAuthorizationHeader();
            if (authorizationHeader != null) {
                tmpInit.headers.set('Authorization', authorizationHeader);
            }
        }
        const response = await fetch(absoluteUrl.toString(), tmpInit);
        return responseconverter(response);
    }
    static setAuthorizationHeaderProvider(authorizationHeaderProvider) {
        this._authorizationHeaderProvider = authorizationHeaderProvider;
    }
    static toURL(u) {
        return u instanceof URL ? u : new URL(u);
    }
    static isTwinfinityUrl(u) {
        return u.host.toLowerCase().startsWith('bim.');
    }
    static requiresAuthorizationHeader(u) {
        return this.isTwinfinityUrl(u) || u.pathname.includes('_ps/api');
    }
}
/** Used with  {@link TwinfinityApiClient.get} to convert responses to correct data. See {@link TwinfinityApiClient.get} for an example. */
export class HttpResponseType {
    _statuses;
    /**
     * By default, {@link TypedResponse.value} conversion methods only assign {@link TypedResponse.value} when {@link Response.status} === `200`
     */
    static default = new HttpResponseType([HttpStatusCode.Ok]);
    /**
     * Converts an array buffer {@link Response} to {@link TypedResponse<ArrayBuffer>}.
     */
    arrayBuffer;
    /**
     * Converts a JSON {@link Response} to {@link TypedResponse}.
     */
    json;
    /**
     * Converts a text {@link Response} to {@link TypedResponse<string>}.
     */
    text;
    /**
     * Converts a blob {@link Response} to {@link TypedResponse<Blob>}.
     */
    blob;
    /**
     * Converts an empty {@link Response} to {@link TypedResponse<undefined>}.
     */
    empty;
    constructor(_statuses) {
        this._statuses = _statuses;
        // Since these properties are almost always passed as function references
        // and we need to keep the 'this' pointer to this class we have to defined the methods
        // like this
        this.arrayBuffer = (r) => this.setValue(r, (r) => r.arrayBuffer());
        this.json = (r) => this.setValue(r, (r) => r.json());
        this.text = (r) => this.setValue(r, (r) => r.text());
        this.blob = (r) => this.setValue(r, (r) => r.blob());
        this.empty = (r) => this.setValue(r, (r) => Promise.resolve(undefined));
    }
    /**
     * Default is that {@link TypedResponse.value} is only valid if {@link Response.status} === `200` for
     * responses sent into {@link arrayBuffer}, {@link json}, {@link text} and {@link blob}. Call this method
     * to create a custom {@link HttpResponseType} where {@link TypedResponse.value} can be assigned for other status values
     * as well.
     * Example: A conflict response may still contain data which should be assigned to {@link TypedResponse.value}.
     * @param statuses
     */
    static status(statuses) {
        return new HttpResponseType(statuses);
    }
    /**
     * Converts an array buffer {@link Response} to {@link TypedResponse<ArrayBuffer>}.
     */
    static arrayBuffer(r) {
        return HttpResponseType.default.arrayBuffer(r);
    }
    /**
     * Converts a JSON {@link Response} to {@link TypedResponse}.
     */
    static json(r) {
        return HttpResponseType.default.json(r);
    }
    /**
     * Converts a text {@link Response} to {@link TypedResponse<string>}.
     */
    static text(r) {
        return HttpResponseType.default.text(r);
    }
    /**
     * Converts a blob {@link Response} to {@link TypedResponse<Blob>}.
     */
    static blob(r) {
        return HttpResponseType.default.blob(r);
    }
    setValue(r, converter) {
        const isValidHttpStatus = this._statuses.find((s) => s === r.status) !== undefined;
        if (isValidHttpStatus) {
            r.value = converter(r);
        }
        else {
            // value is not valid if status was not listed as a ok. In that case
            // we just throw a exception  because user is not supposed to access this
            // property.
            Object.defineProperty(r, 'value', {
                get() {
                    const error = new Error(`HTTP ${r.type} ${r.status} ${r.statusText}`);
                    error.response = r;
                    throw error;
                }
            });
        }
        return r;
    }
}
//# sourceMappingURL=http.js.map