const crypto = require('crypto');
const jwt = require('jsonwebtoken');

const db = require('./utils/data');
const secretKey = require('./secretKey');

function getCurrentSeconds() {
  return Math.floor(Date.now() / 1000);
}

function createTokenId() {
  return crypto.randomBytes(16).toString('hex');
}

function createToken(scopes = {}) {
  return new Promise((resolve, reject) => {
    const payload = {
      jti: createTokenId(),
      iss: 'https://unpkg.com',
      iat: getCurrentSeconds(),
      scopes
    };

    jwt.sign(
      payload,
      secretKey.private,
      { algorithm: 'RS256' },
      (error, token) => {
        if (error) {
          reject(error);
        } else {
          resolve(token);
        }
      }
    );
  });
}

const revokedTokensSet = 'revoked-tokens';

function verifyToken(token) {
  return new Promise((resolve, reject) => {
    const options = { algorithms: ['RS256'] };

    jwt.verify(token, secretKey.public, options, (error, payload) => {
      if (error) {
        reject(error);
      } else {
        if (payload.jti) {
          db.sismember(revokedTokensSet, payload.jti, (error, value) => {
            if (error) {
              reject(error);
            } else {
              resolve(value === 0 ? payload : null);
            }
          });
        } else {
          resolve(null);
        }
      }
    });
  });
}

function revokeToken(token) {
  return verifyToken(token).then(payload => {
    if (payload) {
      return new Promise((resolve, reject) => {
        db.sadd(revokedTokensSet, payload.jti, error => {
          if (error) {
            reject(error);
          } else {
            resolve();
          }
        });
      });
    }
  });
}

function removeAllRevokedTokens() {
  return new Promise((resolve, reject) => {
    db.del(revokedTokensSet, error => {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  });
}

module.exports = {
  createToken,
  verifyToken,
  revokeToken,
  removeAllRevokedTokens
};