"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.XHR = void 0;
const ModelHelper_1 = require("../model/ModelHelper");
const NoAuthService_1 = require("../../icc-x-api/auth/NoAuthService");
const icc_x_api_1 = require("../../icc-x-api");
var XHR;
(function (XHR) {
    class Header {
        constructor(header, data) {
            this.header = header;
            this.data = data;
        }
    }
    XHR.Header = Header;
    class Data {
        constructor(status, contentType, body, responseHeaders = {}) {
            this.statusCode = status;
            this.contentType = contentType;
            this.body = body;
            this.responseHeaders = responseHeaders;
        }
    }
    XHR.Data = Data;
    class XHRError extends Error {
        constructor(url, message, status, errorCode, headers) {
            super(message);
            this.url = url;
            this.statusCode = status;
            this.message = message;
            this.errorCode = errorCode;
            this.headers = headers;
        }
    }
    XHR.XHRError = XHRError;
    function fetchWithTimeout(url, init, timeout = 10000, fetchImpl = typeof window !== 'undefined'
        ? window.fetch
        : typeof self !== 'undefined'
            ? self.fetch
            : fetch) {
        return new Promise((resolve, reject) => {
            // Set timeout timer
            let timer = setTimeout(() => reject({ message: 'Request timed out', status: 'Request timed out' }), timeout);
            fetchImpl(url, init)
                .then((response) => {
                clearTimeout(timer);
                resolve(response);
            })
                .catch((err) => {
                clearTimeout(timer);
                reject(err);
            });
        });
    }
    function sendCommand(method_1, url_1, headers_1) {
        return __awaiter(this, arguments, void 0, function* (method, url, headers, data = '', fetchImpl = typeof window !== 'undefined'
            ? window.fetch
            : typeof self !== 'undefined'
                ? self.fetch
                : fetch, contentTypeOverride, headerProvider = new NoAuthService_1.NoAuthService(), minimumAuthenticationClass = undefined, tryHardToParseJson = false, collectHeaders = []) {
            const authHeaders = yield headerProvider.getAuthHeaders(minimumAuthenticationClass);
            const contentType = headers && headers.find((it) => (it.header ? it.header.toLowerCase() === 'content-type' : false));
            const clientTimeout = headers && headers.find((it) => (it.header ? it.header.toUpperCase() === 'X-CLIENT-SIDE-TIMEOUT' : false));
            const timeout = clientTimeout ? Number(clientTimeout.data) : 600000;
            return fetchWithTimeout(url, Object.assign({
                method: method,
                credentials: 'include',
                headers: (headers !== null && headers !== void 0 ? headers : [])
                    .concat(authHeaders)
                    .filter((h) => {
                    var _a, _b, _c;
                    return (((_a = h.header) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== 'content-type' || h.data !== 'multipart/form-data') &&
                        ((_b = h.header) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== 'X-CLIENT-SIDE-TIMEOUT' &&
                        ((_c = h.header) === null || _c === void 0 ? void 0 : _c.toLowerCase()) !== 'force-authentication';
                })
                    .reduce((acc, h) => {
                    acc[h.header] = h.data;
                    return acc;
                }, { 'X-Requested-With': 'XMLHttpRequest' }),
            }, method === 'POST' || method === 'PUT'
                ? {
                    body: !contentType || contentType.data === 'application/json'
                        ? JSON.stringify(data, (k, v) => {
                            return v instanceof ArrayBuffer || v instanceof Uint8Array ? (0, ModelHelper_1.ua2b64)(v) : v;
                        })
                        : data,
                }
                : {}), timeout, fetchImpl).then(function (response) {
                return __awaiter(this, void 0, void 0, function* () {
                    if (response.status === 401) {
                        headerProvider.invalidateHeader(new XHRError(url, yield response.text(), response.status, response.statusText, response.headers));
                        const requiredAuthLevelHeader = response.headers.get('Icure-Minimum-Required-Auth-Level');
                        return sendCommand(method, url, headers, data, fetchImpl, contentTypeOverride, headerProvider, requiredAuthLevelHeader ? parseInt(requiredAuthLevelHeader) : undefined, tryHardToParseJson, collectHeaders);
                    }
                    else if (response.status >= 400) {
                        const error = { error: response.statusText, message: yield response.text(), status: response.status };
                        console.warn(`XHR Error: ${method} ${url} - ${error.status} - ${error.error}`, error.message);
                        throw new XHRError(url, error.message, error.status, error.error, response.headers);
                    }
                    else {
                        const ct = contentTypeOverride || response.headers.get('content-type') || 'text/plain';
                        return (ct.startsWith('application/json')
                            ? tryHardToParseJson ? response.arrayBuffer().then((ab) => __awaiter(this, void 0, void 0, function* () {
                                try {
                                    return JSON.parse((0, icc_x_api_1.ua2utf8)(ab));
                                }
                                catch (e) {
                                    console.warn('Error parsing JSON fallback on ua2string', e);
                                    return JSON.parse((0, ModelHelper_1.ua2string)(ab).replace(/[\u0000-\u001F\u007F-\u009F]/g, ""));
                                }
                            })) : response.json()
                            : ct.startsWith('application/xml') || ct.startsWith('text/')
                                ? response.text()
                                : response.arrayBuffer()).then((d) => {
                            const responseHeaders = {};
                            if (collectHeaders.length) {
                                const prefixPatterns = [];
                                for (const h of collectHeaders) {
                                    if (h.endsWith('*')) {
                                        prefixPatterns.push(h.slice(0, -1).toLowerCase());
                                    }
                                    else if (response.headers.has(h)) {
                                        responseHeaders[h] = [response.headers.get(h)];
                                    }
                                }
                                if (prefixPatterns.length) {
                                    response.headers.forEach((value, key) => {
                                        const lowerKey = key.toLowerCase();
                                        if (prefixPatterns.some((prefix) => lowerKey.startsWith(prefix))) {
                                            if (!responseHeaders[key]) {
                                                responseHeaders[key] = [];
                                            }
                                            responseHeaders[key].push(value);
                                        }
                                    });
                                }
                            }
                            return new Data(response.status, ct, d, responseHeaders);
                        });
                    }
                });
            });
        });
    }
    XHR.sendCommand = sendCommand;
})(XHR || (exports.XHR = XHR = {}));
//# sourceMappingURL=XHR.js.map