/* eslint no-param-reassign: off, import/prefer-default-export: off */
const jwt = require('jsonwebtoken');
const { cert } = require('./config');

function Timeout(fn, interval) {
  this.id = setTimeout(fn, interval);
  this.cleared = false;
  this.clear = () => {
    this.cleared = true;
    clearTimeout(this.id);
  };
}

const interval = (fn, initialTTL, output = {}) => {
  const getTimeout = ttl =>
    new Timeout(async () => {
      let TTL;
      try {
        if (this.cleared) {
          return;
        }
        TTL = await Promise.resolve(fn());
      } catch (error) {
        console.error(error);
        TTL = initialTTL;
      }
      if (this.cleared) {
        return;
      }
      const nextTTL = parseInt(TTL, 10) || initialTTL;
      interval(fn, nextTTL, output);
    }, ttl);

  const getOutput = (timeout) => {
    const stop = () => {
      if (timeout) {
        timeout.clear();
      }
    };
    return {
      start: (ttl) => {
        if (timeout && !timeout.cleared) {
          throw new Error('Interval is already started');
        }
        const newOutput = getOutput(getTimeout(ttl || initialTTL));
        Object.assign(output, getOutput(newOutput));
        return output;
      },
      startNow: () => {
        if (timeout && !timeout.cleared) {
          throw new Error('Interval is already started');
        }
        Object.assign(output, getOutput(getTimeout(0)));
        return output;
      },
      stop,
      restart: (ttl) => {
        stop();
        Object.assign(output, getOutput(getTimeout(ttl || initialTTL)));
        return output;
      },
      restartNow: () => {
        stop();
        Object.assign(output, getOutput(getTimeout(0)));
        return output;
      },
    };
  };

  if (Object.keys(output).length === 0) {
    Object.assign(output, getOutput(null));
  } else {
    Object.assign(output, getOutput(getTimeout(initialTTL)));
  }
  return output;
};

const generateRandomString = (length) => {
  let text = '';
  const possible =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-,;:!?./§ù*^$£%µ&"\'(=';

  for (let i = 0; i < length; i += 1) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return text;
};

const createSignedJWT = () =>
  jwt.sign({ state: generateRandomString(20) }, cert, { algorithm: 'RS256' });

module.exports = { interval, createSignedJWT };