#!/usr/bin/env node

/**
 * Module dependencies.
 */

const util = require('./util');
const program = require('commander');
const inquirer = require('inquirer');
const mongoose = require('../services/mongoose');
const SettingModel = require('../models/setting');
const MODERATION_OPTIONS = require('../models/enum/moderation_options');
const SettingsService = require('../services/settings');
const SetupService = require('../services/setup');
const UsersService = require('../services/users');
const MigrationService = require('../services/migration');
const { ErrSettingsInit, ErrSettingsNotInit } = require('../errors');
const Context = require('../graph/context');

// Register the shutdown criteria.
util.onshutdown([() => mongoose.disconnect()]);

//==============================================================================
// Setting up the program command line arguments.
//==============================================================================

program
  .description('runs the setup wizard to setup the application')
  .option('--defaults', 'apply defaults for config instead of prompting')
  .parse(process.argv);

//==============================================================================
// Setup the application
//==============================================================================

const performSetup = async () => {
  // Get the current settings, we are expecing an error here.
  try {
    // Try to get the settings.
    await SettingsService.retrieve();

    // We should NOT have gotten a settings object, this means that the
    // application is already setup. Error out here.
    throw new ErrSettingsInit();
  } catch (err) {
    // If the error is `not init`, then we're good, otherwise, it's something
    // else.
    if (!err instanceof ErrSettingsNotInit) {
      throw err;
      return;
    }
  }

  if (program.defaults) {
    await SettingsService.init();

    // Get the migrations to run.
    let migrations = await MigrationService.listPending();

    // Perform all migrations.
    await MigrationService.run(migrations);

    console.log('Settings created.');
    console.log('\nTalk is now installed!');

    return;
  }

  // Create the base settings model.
  let settings = new SettingModel();

  console.log(
    "\nWe'll ask you some questions in order to setup your installation of Talk.\n"
  );

  let answers = await inquirer.prompt([
    {
      type: 'input',
      name: 'organizationName',
      message: 'Organization Name',
      default: settings.organizationName,
      validate: input => {
        if (input && input.length > 0) {
          return true;
        }

        return 'Organization Name is required.';
      },
    },
    {
      type: 'list',
      choices: MODERATION_OPTIONS,
      name: 'moderation',
      default: settings.moderation,
      message: 'Select a moderation mode',
    },
    {
      type: 'confirm',
      name: 'requireEmailConfirmation',
      default: settings.requireEmailConfirmation,
      message: 'Should emails always be confirmed',
    },
  ]);

  // Update the settings that were changed.
  Object.keys(answers).forEach(key => {
    if (answers[key] !== undefined) {
      settings[key] = answers[key];
    }
  });

  answers = await inquirer.prompt([
    {
      type: 'confirm',
      name: 'inputWhitelistedDomains',
      default: true,
      message: 'Would you like to specify a whitelisted domain',
    },
    {
      type: 'input',
      name: 'whitelistedDomain',
      message: 'Whitelisted Domain',
      when: ({ inputWhitelistedDomains }) => inputWhitelistedDomains,
      validate: input => {
        if (input && input.length > 0) {
          return true;
        }

        return 'Whitelisted Domain cannot be empty.';
      },
    },
  ]);

  if (answers.inputWhitelistedDomains) {
    settings.domains.whitelist = [answers.whitelistedDomain];
  }

  console.log("\nWe'll ask you some questions about your first admin user.\n");

  let { username, email } = await inquirer.prompt([
    {
      type: 'input',
      name: 'username',
      message: 'Username',
      filter: username => {
        return UsersService.isValidUsername(username, false).catch(err => {
          throw err.message;
        });
      },
    },
    {
      name: 'email',
      message: 'Email',
      format: 'email',
      validate: value => {
        if (value && value.length >= 3) {
          return true;
        }

        return 'Email is required';
      },
    },
  ]);

  let password = '';
  while (!password) {
    answers = await inquirer.prompt([
      {
        name: 'password',
        message: 'Password',
        type: 'password',
        filter: password => {
          try {
            UsersService.isValidPassword(password);
          } catch (err) {
            throw err.message;
          }

          return password;
        },
      },
      {
        name: 'confirmPassword',
        message: 'Confirm Password',
        type: 'password',
      },
    ]);

    if (answers.password !== answers.confirmPassword) {
      console.error('Passwords do not match');
    } else {
      password = answers.password;
    }
  }

  const ctx = Context.forSystem();
  let { user: newUser } = await SetupService.setup(ctx, {
    settings: settings.toObject(),
    user: {
      email,
      username,
      password,
    },
  });

  console.log('Settings created.');
  console.log(`User ${newUser.id} created.`);
  console.log('\nTalk is now installed!');
  console.log(
    '\nWe recommend adding TALK_INSTALL_LOCK=TRUE to your environment to turn off the dynamic setup.'
  );
};

// Start the setup process.
performSetup()
  .then(() => {
    util.shutdown();
  })
  .catch(e => {
    console.error(e);
    util.shutdown(1);
  });
