export const fetchMixin = function(superClass) {
  return class extends superClass {
    constructor() {
      super();
      this.__abortControllers = new Map();
    }

    get(url, overwrite = true) {
      return this.do('GET', url, null, overwrite);
    }

    head(url) {
      return fetch(url, { method: 'HEAD' });
    }

    post(url, data, overwrite = true) {
      return this.do('POST', url, data, overwrite);
    }

    async do(method, url, data, overwrite = true) {
      this.__cancelRunningRequest(method, url);

      if (overwrite === true) {
        const ac = new AbortController();
        this.__abortControllers.set(`${method}:${url}`, ac);
      }

      try {
        const reqOptions = {
          method,
          signal: overwrite ? this.__abortControllers.get(`${method}:${url}`).signal : null,
          mode: 'cors',
          cache: 'no-cache',
          headers: {
            'Content-Type': 'application/json; charset=utf-8'
          },
          referrer: 'no-referrer',
          body: data ? JSON.stringify(data) : undefined
        };

        document.dispatchEvent(new CustomEvent('before-request', { detail: reqOptions, bubbles: true, composed: true }));

        const resp = await fetch(url, reqOptions);
        this.__abortControllers.delete(`${method}:${url}`);
        if (resp.status === 500) {
          console.error(resp);
        }

        if (resp.status !== 200) {
          document.dispatchEvent(new CustomEvent('request-error', { detail: resp, bubbles: true, composed: true }));
        }

        return await resp.json();
      } catch (err) {
        if (err.name === 'AbortError') {
          return { statusCode: -1, error: err };
        } else {
          this.__abortControllers.delete(`${method}:${url}`);
          return { statusCode: null, error: err };
        }
      }
    }

    isInFlight(method, url) {
      if (url === undefined) {
        console.error('Missing url parameter');
        return false;
      }

      return Boolean(this.__abortControllers.get(`${method}:${url}`));
    }

    __cancelRunningRequest(method, url) {
      if (this.__abortControllers.has(`${method}:${url}`)) {
        try {
          this.__abortControllers.get(`${method}:${url}`).abort();
        } catch (err) { }
        this.__abortControllers.delete(`${method}:${url}`);
      }
    }
  };
};