const axios = require('axios');
const check = require('check-types');
const debug = require('../debug').extend('low');

class AuthlessLowLevelClient {
  /*
   * retries: how many times it should be retried
   * uri: authless server uri
   * 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.number(params.retries, 'expected params.retries to be a number');
    check.assert.string(params.uri, 'expected params.uri to be a URI string');
    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);
    if (!params.responseFormat) params.responseFormat = 'json';
    const cacheResult = await this.checkCache(params.u, debug.extend('url'));
    if (cacheResult) return cacheResult;
    try {
      const data = await axios.get(`${this.uri}/url`, {params}).
        then(response => response.data);
      await this.writeCache(params.u, data, debug.extend('url'));
      return data;
    } catch (e) {
      if (retryCounter < this.retries) {
        debug.extend('url').extend('error')(`retry/${retryCounter + 1}: ${e.message}`);
        return this.url(params, retryCounter + 1);
      }
      debug.extend('url').extend('error')(`retried ${this.retries} times; its not working`);
      throw e;
    }
  }

  async search (params) {
    debug.extend('search')(params.q);
    if (!params.responseFormat) params.responseFormat = 'json';
    return axios.get(`${this.uri}/search`, {params}).
      then(response => response.data);
  }
}

module.exports = { AuthlessLowLevelClient };
