#!/usr/bin/env node

import * as inquirer from 'inquirer';
import Constants from '../utils/Constants';
import StdOutUtil from '../utils/StdOutUtil';
import { isIpAddress } from '../utils/ValidationsHandler';
import { IMachine } from '../models/storage/StoredObjects';
import CliApiManager from '../api/CliApiManager';
import Utils from '../utils/Utils';
import CliHelper from '../utils/CliHelper';
import StorageHelper from '../utils/StorageHelper';
import ErrorFactory from '../utils/ErrorFactory';
import SpinnerHelper from '../utils/SpinnerHelper';

let newPasswordFirstTry: string | undefined = undefined;
let lastWorkingPassword: string = Constants.DEFAULT_PASSWORD;
let serverIpAddress = '';

let captainMachine: IMachine = {
	authToken: '',
	baseUrl: '',
	name: ''
};

const questions = [
	{
		type: 'list',
		name: 'hasInstalledCaptain',
		message:
			'Have you already installed Captain on your server by running the following line:' +
			'\nmkdir /captain && docker run -p 80:80 -p 443:443 -p 3000:3000 -v /var/run/docker.sock:/var/run/docker.sock dockersaturn/captainduckduck ?',
		default: 'Yes',
		choices: [ 'Yes', 'No' ],
		filter: (value: string) => {
			const answerFromUser = value.trim();

			if (answerFromUser === 'Yes') return answerFromUser;

			StdOutUtil.printMessage('\n\nCannot start the setup process if Captain is not installed.');

			StdOutUtil.printMessageAndExit(
				'Please read tutorial on CaptainDuckDuck.com to learn how to install CaptainDuckDuck on a server.'
			);
		}
	},
	{
		type: 'input',
		default: Constants.SAMPLE_IP,
		name: 'captainAddress',
		message: 'Enter IP address of your captain server:',
		filter: async (value: string) => {
			const ipFromUser = value.trim();

			if (ipFromUser === Constants.SAMPLE_IP || !isIpAddress(ipFromUser)) {
				StdOutUtil.printError(`\nThis is an invalid IP Address: ${ipFromUser}`, true);
			}

			try {
				// login using captain42. and set the ipAddressToServer
				captainMachine.baseUrl = `http://${ipFromUser}:3000`;
				await CliApiManager.get(captainMachine).getAuthToken(lastWorkingPassword);
				serverIpAddress = ipFromUser;
			} catch (e) {
				// User may have used a different default password
				if (e.captainStatus === ErrorFactory.STATUS_WRONG_PASSWORD) return '';
				StdOutUtil.errorHandler(e);
			}

			return ipFromUser;
		}
	},
	{
		type: 'password',
		name: 'captainOriginalPassword',
		message: 'Enter your current password:',
		when: () => !captainMachine.authToken, // The default password didn't work
		filter: async (value: string) => {
			try {
				await CliApiManager.get(captainMachine).getAuthToken(value);
				lastWorkingPassword = value;
				return '';
			} catch (e) {
				StdOutUtil.errorHandler(e);
			}
		}
	},
	{
		type: 'input',
		name: 'captainRootDomain',
		message:
			'Enter a root domain for this Captain server. For example, enter test.yourdomain.com if you' +
			' setup your DNS to point *.test.yourdomain.com to ip address of your server.',
		filter: async (value: string) => {
			const captainRootDomainFromUser = value.trim();
			try {
				await CliApiManager.get(captainMachine).updateRootDomain(captainRootDomainFromUser);
				captainMachine = Utils.copyObject(captainMachine);
				captainMachine.baseUrl = `http://captain.${captainRootDomainFromUser}`;
			} catch (e) {
				StdOutUtil.printError('\n\n');
				if (e.captainStatus === ErrorFactory.VERIFICATION_FAILED) {
					if (captainRootDomainFromUser.indexOf('/') >= 0) {
						StdOutUtil.printError(
							'DO NOT include http in your base domain, it should be just plain domain, e.g., test.domain.com'
						);
					}

					if (captainRootDomainFromUser.indexOf('*') >= 0) {
						StdOutUtil.printError(
							'DO NOT include * in your base domain, it should be just plain domain, e.g., test.domain.com'
						);
					}

					StdOutUtil.printError(
						`\n\nCannot verify that http://captain.${captainRootDomainFromUser} points to your server IP.\n` +
							`\nAre you sure that you set *.${captainRootDomainFromUser} points to ${serverIpAddress}\n\n` +
							`Double check your DNS. If everything looks correct, note that, DNS changes take up to 24 hrs to work properly. Check with your Domain Provider.`
					);
				}
				StdOutUtil.errorHandler(e);
			}

			return captainRootDomainFromUser;
		}
	},
	{
		type: 'password',
		name: 'newPasswordFirstTry',
		message: 'Enter a new password:',
		filter: (value: string) => {
			newPasswordFirstTry = value;

			if (!newPasswordFirstTry) {
				StdOutUtil.printError('Password empty.', true);
				throw new Error('Password empty');
			}

			return value;
		}
	},
	{
		type: 'password',
		name: 'newPassword',
		message: 'Enter your new password again:',
		filter: async (value: string) => {
			const confirmPasswordValueFromUser = value;

			if ((newPasswordFirstTry !== confirmPasswordValueFromUser)) {
				StdOutUtil.printError('Passwords do not match. Try serversetup again.', true);
				throw new Error('Password mismatch');
			}

			return '';
		}
	},
	{
		type: 'input',
		name: 'emailAddress',
		message: "Enter your 'valid' email address to enable HTTPS: ",
		filter: async (value: string) => {
			const emailAddressFromUser = value.trim();
			let forcedSsl = false;
			try {
				SpinnerHelper.start('Enabling SSL... Takes a few seconds...');
				await CliApiManager.get(captainMachine).enableRootSsl(emailAddressFromUser);

				captainMachine = Utils.copyObject(captainMachine);
				captainMachine.baseUrl = captainMachine.baseUrl.replace('http://', 'https://');

				await CliApiManager.get(captainMachine).forceSsl(true);
				forcedSsl = true;
				await CliApiManager.get(captainMachine).changePass(lastWorkingPassword, newPasswordFirstTry!);
				lastWorkingPassword = newPasswordFirstTry!;
				await CliApiManager.get(captainMachine).getAuthToken(lastWorkingPassword);
				SpinnerHelper.stop();
			} catch (e) {
				if (forcedSsl) {
					StdOutUtil.printError(
						'Server is setup, but password was not changed due to an error. You cannot use serversetup again.'
					);
					StdOutUtil.printError(
						`Instead, go to ${captainMachine.baseUrl} and change your password on settings page.`
					);
					StdOutUtil.printError(
						`Then, Use captainduckduck login on your local machine to connect to your server.`
					);
				}
				SpinnerHelper.fail();
				StdOutUtil.errorHandler(e);
			}

			return emailAddressFromUser;
		}
	},
	{
		type: 'input',
		name: 'captainName',
		message: 'Enter a name for this Captain machine:',
		default: CliHelper.get().findDefaultCaptainName(),
		validate: (value: string) => {
			const newMachineName = value.trim();

			let errorMessage = undefined;
			if (StorageHelper.get().findMachine(newMachineName)) {
				return `${newMachineName} already exist. If you want to replace the existing entry, you have to first use <logout> command, and then re-login.`;
			}

			if (CliHelper.get().isNameValid(newMachineName)) {
				captainMachine.name = newMachineName;
				return true;
			}

			return 'Please enter a valid Captain Name. Small letters, numbers, single hyphen.';
		}
	}
];

async function serversetup() {
	StdOutUtil.printMessage('\nSetup your Captain server\n');

	const answersIgnore = await inquirer.prompt(questions);

	StorageHelper.get().saveMachine(captainMachine);

	StdOutUtil.printMessage(`\n\nCaptain is available at ${captainMachine.baseUrl}`);

	StdOutUtil.printMessage('\nFor more details and docs see http://www.captainduckduck.com\n\n');
}

export default serversetup;
