// хелпер-класс для вызовов fetch с заданного хоста API с заданными заголовками по умолчанию
// backend.send('POST', '/updates/').then(data => { /* ... */ })

// https://tools.ietf.org/html/rfc7231#section-4.2.3
const CacheableMethods = [ 'GET', 'HEAD' ];

class APIClient {
    // если в инстансе класса задан cacheLifeTime,
    // то при вызове дублирующего запроса в send() в течение cacheLifeTime
    // send() не создаст новый запрос, а вернёт закэшированный
    constructor(urlBase = '', options = {}, cacheLifeTime) {
        this.urlBase = urlBase;
        this.options = options;

        if (typeof cacheLifeTime === 'number') {
            this.cacheLifeTime = cacheLifeTime;
            this.cache = {};
        }

        this._resolve = jsonResolve;
    }

    send(method, path = '', options = {}) {
        let url = new URL(this.urlBase + path, window.location.origin);
        let requestOptions = {
            ...(typeof this.options === 'function' ? this.options(options) : this.options),
            ...options,
            method
        };

        if (requestOptions.query) {
            let searchParams = new URLSearchParams();

            url.searchParams.forEach((value, key) => {
                searchParams.append(key, value);
            });

            Object.entries(requestOptions.query).forEach(([ key, value ]) => {
                searchParams.append(key, value);
            });

            url.search = searchParams;

            delete requestOptions.query;
        }

        let cacheKey = Boolean(this.cache && CacheableMethods.includes(method)) &&
            JSON.stringify({ url: url.href, ...requestOptions });

        if (cacheKey && this.cache[cacheKey]) {
            return this.cache[cacheKey];
        }

        let req = new Request(url.href, requestOptions);

        let output = fetch(req)
            .then(this._resolve && (res => this._resolve(req, res)))
            .catch(this._reject && (error => this._reject(error, req)));

        if (cacheKey) {
            this.cache[cacheKey] = output;

            setTimeout(() => {
                delete this.cache[cacheKey];
            }, this.cacheLifeTime);
        }

        return output;
    }

    resolve(callback) {
        this._resolve = callback;

        return this;
    }

    reject(callback) {
        this._reject = callback;

        return this;
    }
}

export function jsonResolve(req, res) {
    let { ok, status, statusText } = res;

    return res.json().then(body => ({ ok, status, statusText, body }));
}

export default APIClient;
