const check = require('check-types');
const { AuthlessLowLevelClient } = require('./authlessLowLevelClient.js');
const { AuthlessRouter } = require('@authless/router');
const debug = require('../debug').extend('high');

const convertToRouterURL = url => {
  const regex = /http:\/|https:\//gum;
  return url.replace(regex, '');
}

class AuthlessClient {
  /*
   * lowLevelClient: lowLevelClient instance
   * retries: how many times it should be retried
   * router: AuthlessRouter
   * cache: {
   *  existsFn: a function that checks if it exists (takes url as input)
   *  retrieveFn: a function that retrieves from the cache (takes url as input)
   *  removeFn: a function that removes it from the cache (takes url as input)
   * }
   */
  constructor (params) {
    check.assert.instance(params.lowLevelClient, AuthlessLowLevelClient);
    check.assert.number(params.retries);
    check.assert.instance(params.router, AuthlessRouter);
    check.assert(check.any([
      check.undefined(params.cache),
      check.object(params.cache)
    ]), 'expected params.cache to either be undefined or be a proper object');
    Object.assign(this, params);
  }

  // TODO: refactor together with low-level checkCache into own Cache class
  async checkCache (url, _debug = debug.extend('cache')) {
    if (this.cache) {
      if (await this.cache.existsFn(url)) {
        _debug.extend('cache').extend('succes')(url);
        return this.cache.retrieveFn(url);
      }
      _debug.extend('cache').extend('miss')(url);
    }
    return false;
  }

  // TODO: refactor together with low-level writeCache into own Cache class
  async writeCache (url, data, _debug = debug.extend('cache')) {
    if (this.cache) {
      _debug.extend('cache').extend('store')(url);
      return this.cache.storeFn(url, data);
    }
    return false;
  }

  async url (params, retryCounter = 0) {
    debug.extend('url')(params.u);
    let data = await this.checkCache(params.u, debug.extend('url'));
    if (data) return data
    try {
      data = await this.lowLevelClient.url(params);
      const route = this.router.find('GET', convertToRouterURL(data.main.url));
      if (route) {
        const service = route.handler();
        if (service.extract) {
          const extracted = service.extract(data);
          await this.writeCache(params.u, extracted, debug.extend('url'));
          return extracted;
        }
      }
      return data;
    } catch (e) {
      if (retryCounter < this.retries) return this.url(params, retryCounter + 1);
      debug.extend('url').extend('error')(`retried ${this.retries} times; its not working`);
      throw e;
    }
  }
}

module.exports = { AuthlessClient };
