const crypto = require('crypto');
const inquirer = require('inquirer');
const OTPAuth = require('otpauth');
const { copy } = require('./utils.js');
const phrases = {
  enterPassword: 'Enter a password:',
  enterUsername: 'Enter a username:',
  enterAppName: 'Enter the app name:',
}
const getAlgorithm = async () => {
  let { algorithm } = await inquirer.prompt([
    {
      type: 'list',
      name: 'algorithm',
      message: 'Which hashing algorithm do you want to use?',
      default: 'SHA1',
      choices: [
        'SHA1',
        'SHA224',
        'SHA256',
        'SHA384',
        'SHA512',
        'SHA3-224',
        'SHA3-256',
        'SHA3-384',
        'SHA3-512',
        'Other'
      ]
    }
  ]);
  if (algorithm === 'Other') {
    const result = await inquirer.prompt([
      {
        type: 'input',
        name: 'algorithm',
        message: 'Enter the hashing algorithm you want to use:'
      }
    ]);
    algorithm = result.algorithm;
  }
  const { digits, period } = await inquirer.prompt([
    {
      type: 'number',
      name: 'digits',
      default: 6,
      message: 'Enter the number of digits the one-time password should have:'
    },
    {
      type: 'number',
      name: 'period',
      default: 30,
      message: 'Enter how long the one-time password should be valid (in seconds):'
    }
  ])
  return { algorithm, digits, period};
};
const generateSecret = ({ app, username, algorithm, digits, period }) => {
  const secret = new OTPAuth.Secret();
  const totp = new OTPAuth.TOTP({
    issuer: app,
    label: username,
    algorithm,
    digits,
    period,
    secret
  });
  const url = totp.toString();
  const config = { mfa: secret.base32 };
  config.app = app;
  config.url = url;
  if (algorithm !== 'SHA1') {
    config.mfaAlgorithm = algorithm;
  }
  if (digits != 6) {
    config.mfaDigits = digits;
  }
  if (period != 30) {
    config.mfaPeriod = period;
  }
  return { config };
};
const showQR = text => {
  const QRCode = require('qrcode');
  QRCode.toString(text, { type: 'terminal' }, (err, url) => {
    console.log(
      '\n------------------------------------------------------------------------------' +
      `\n\n${url}`
    );
  });
};

const showInstructions = ({ app, username, passwordCopied, encrypt, config }) => {
  const {secret, url} = config;
  const mfaJSON = {...config};
  delete mfaJSON.url;
  let orderCounter = 0;
  const getOrder = () => {
    orderCounter++;
    return orderCounter;
  }
  console.log(
    '------------------------------------------------------------------------------' +
    '\n\nFollow these steps to complete the set-up:'
  );

  console.log(
    `\n${getOrder()}. Add the following settings for user "${username}" ${app ? `in app "${app}" ` : '' }to the Parse Dashboard configuration.` +
    `\n\n   ${JSON.stringify(mfaJSON)}`
  );

  if (passwordCopied) {
    console.log(
      `\n${getOrder()}. Securely store the generated login password that has been copied to your clipboard.`
    );
  }

  if (secret) {
    console.log(
      `\n${getOrder()}. Open the authenticator app to scan the QR code above or enter this secret code:` +
      `\n\n   ${secret}` +
      '\n\n   If the secret code generates incorrect one-time passwords, try this alternative:' +
      `\n\n   ${url}` +
      `\n\n${getOrder()}. Destroy any records of the QR code and the secret code to secure the account.`
    );
  }

  if (encrypt) {
    console.log(
      `\n${getOrder()}. Make sure that "useEncryptedPasswords" is set to "true" in your dashboard configuration.` +
      '\n   You chose to generate an encrypted password for this user.' +
      '\n   Any existing users with non-encrypted passwords will require newly created, encrypted passwords.'
    );
  }
  console.log(
    '\n------------------------------------------------------------------------------\n'
  );
}

module.exports = {
  async createUser() {
    const data = {};

    console.log('');
    const { username, password } = await inquirer.prompt([
      {
        type: 'input',
        name: 'username',
        message: phrases.enterUsername
      },
      {
        type: 'confirm',
        name: 'password',
        message: 'Do you want to auto-generate a password?'
      }
    ]);
    data.user = username;
    if (!password) {
      const { password } = await inquirer.prompt([
        {
          type: 'password',
          name: 'password',
          message: phrases.enterPassword
        }
      ]);
      data.pass = password;
    } else {
      const password = crypto.randomBytes(20).toString('base64');
      data.pass = password;
    }
    const { mfa, encrypt } = await inquirer.prompt([
      {
        type: 'confirm',
        name: 'encrypt',
        message: 'Should the password be encrypted? (strongly recommended, otherwise it is stored in clear-text)'
      },
      {
        type: 'confirm',
        name: 'mfa',
        message: 'Do you want to enable multi-factor authentication?'
      }
    ]);
    if (encrypt) {
      // Copy the raw password to clipboard
      copy(data.pass);

      // Encrypt password
      const bcrypt = require('bcryptjs');
      const salt = bcrypt.genSaltSync(10);
      data.pass = bcrypt.hashSync(data.pass, salt);
    }
    const config = {};
    if (mfa) {
      const { app } = await inquirer.prompt([
        {
          type: 'input',
          name: 'app',
          message: phrases.enterAppName
        }
      ]);
      const { algorithm, digits, period } = await getAlgorithm();
      const secret  = generateSecret({ app, username, algorithm, digits, period });
      Object.assign(config, secret.config);
      showQR(secret.config.url);
    }
    config.user = data.user;
    config.pass = data.pass ;
    showInstructions({ app: data.app, username, passwordCopied: true, encrypt, config });
  },
  async createMFA() {
    console.log('');
    const { username, app } = await inquirer.prompt([
      {
        type: 'input',
        name: 'username',
        message:
          'Enter the username for which you want to enable multi-factor authentication:'
      },
      {
        type: 'input',
        name: 'app',
        message: phrases.enterAppName
      }
    ]);
    const { algorithm, digits, period } = await getAlgorithm();

    const { config } = generateSecret({ app, username, algorithm, digits, period });
    showQR(config.url);
    // Compose config
    showInstructions({ app, username, config });
  }
};
