/**
 * @license
 * Copyright 2020 Balena Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Command from '@oclif/command';
import { InsufficientPrivilegesError } from './errors';

export default abstract class BalenaCommand extends Command {
	/**
	 * When set to true, command will be listed in `help`,
	 * otherwise listed in `help --verbose` with secondary commands.
	 */
	public static primary = false;

	/**
	 * Require elevated privileges to run.
	 * When set to true, command will exit with an error
	 * if executed without root on Mac/Linux
	 * or if executed by non-Administrator on Windows.
	 */
	public static root = false;

	/**
	 * Require authentication to run.
	 * When set to true, command will exit with an error
	 * if user is not already logged in.
	 */
	public static authenticated = false;

	/**
	 * Accept piped input.
	 * When set to true, command will read from stdin during init
	 * and make contents available on member `stdin`.
	 */
	public static readStdin = false;

	public stdin: string;

	/**
	 * Throw InsufficientPrivilegesError if not root on Mac/Linux
	 * or non-Administrator on Windows.
	 *
	 * Called automatically if `root=true`.
	 * Can be called explicitly by command implementation, if e.g.:
	 *  - check should only be done conditionally
	 *  - other code needs to execute before check
	 */
	protected static async checkElevatedPrivileges() {
		const isElevated = await (await import('is-elevated'))();
		if (!isElevated) {
			throw new InsufficientPrivilegesError(
				'You need root/admin privileges to run this command',
			);
		}
	}

	/**
	 * Throw NotLoggedInError if not logged in.
	 *
	 * Called automatically if `authenticated=true`.
	 * Can be called explicitly by command implementation, if e.g.:
	 *  - check should only be done conditionally
	 *  - other code needs to execute before check
	 *
	 *  Note, currently public to allow use outside of derived commands
	 *  (as some command implementations require this. Can be made protected
	 *  if this changes).
	 */
	public static async checkLoggedIn() {
		await (await import('./utils/patterns')).checkLoggedIn();
	}

	/**
	 * Read stdin contents and make available to command.
	 *
	 * This approach could be improved in the future to automatically set argument
	 * values from stdin based in configuration, minimising command implementation.
	 */
	protected async getStdin() {
		this.stdin = await (await import('get-stdin'))();
	}

	protected async init() {
		const ctr = this.constructor as typeof BalenaCommand;

		if (ctr.root) {
			await BalenaCommand.checkElevatedPrivileges();
		}

		if (ctr.authenticated) {
			await BalenaCommand.checkLoggedIn();
		}

		if (ctr.readStdin) {
			await this.getStdin();
		}
	}
}