<?php

namespace App\Console\Commands;

use App\Utilities\Installer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;

class Install extends Command
{
    const CMD_SUCCESS = 0;
    const CMD_ERROR = 1;
    const OPT_DB_HOST = 'db-host';
    const OPT_DB_PORT = 'db-port';
    const OPT_DB_NAME = 'db-name';
    const OPT_DB_USERNAME = 'db-username';
    const OPT_DB_PASSWORD = 'db-password';
    const OPT_DB_PREFIX = 'db-prefix';
    const OPT_COMPANY_NAME = 'company-name';
    const OPT_COMPANY_EMAIL = 'company-email';
    const OPT_ADMIN_EMAIL = 'admin-email';
    const OPT_ADMIN_PASSWORD = 'admin-password';
    const OPT_LOCALE = 'locale';
    const OPT_NO_INTERACTION = 'no-interaction';

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'install
                            {--db-host=localhost : Database host}
                            {--db-port=3306 : Port of the database host}
                            {--db-name= : Name of the database}
                            {--db-username=root : Username to use to access the database}
                            {--db-password= : Password to use to access the database}
                            {--db-prefix= : Table name prefix}
                            {--company-name=My Company : Name of the company}
                            {--company-email=my@company.com : Email of the company}
                            {--admin-email= : Admin user email}
                            {--admin-password= : Admin user password}
                            {--locale=en-GB : Language used in the app}
                            ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Allows to install Akaunting directly through CLI';

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        if (($missing_options = $this->getMissingOptions()) && $this->option(self::OPT_NO_INTERACTION)) {
            $this->line('❌ Some options are missing and --no-interaction is present. Please run the following command for more information :');
            $this->line('❌ php artisan help install');
            $this->line('❌ Missing options are : ' . implode(', ', $missing_options));

            return self::CMD_ERROR;
        }

        $this->line('Setting locale ' . $this->locale);
        Session::put(self::OPT_LOCALE, $this->locale);
        app()->setLocale($this->locale);

        $this->prompt();

        // Create the .env file
        Installer::createDefaultEnvFile();

        $this->line('Creating database tables');
        if (! $this->createDatabaseTables()) {
            return self::CMD_ERROR;
        }

        DB::transaction(function () {
            $this->line('Creating company');
            Installer::createCompany($this->company_name, $this->company_email, $this->locale);

            $this->line('Creating admin');
            Installer::createUser($this->admin_email, $this->admin_password, $this->locale);
        });

        $this->line('Applying the final touches');
        Installer::finalTouches();

        return self::CMD_SUCCESS;
    }

    /**
     * Check that all options are presents. otherwise returns an array of the missing options
     */
    private function getMissingOptions()
    {
        $missing_options = [];

        $constants = [
            'OPT_LOCALE',
            'OPT_DB_PORT',
            'OPT_DB_HOST',
            'OPT_DB_NAME',
            'OPT_DB_USERNAME',
            'OPT_DB_PASSWORD',
            'OPT_DB_PREFIX',
            'OPT_COMPANY_NAME',
            'OPT_COMPANY_EMAIL',
            'OPT_ADMIN_EMAIL',
            'OPT_ADMIN_PASSWORD'
        ];

        $allowed_empty = ['db_password', 'db_prefix'];

        foreach ($constants as $const) {
            $option = constant("self::$const");

            $property = str_replace('-', '_', $option);

            $this->$property = $this->option($option);

            if (!empty($this->$property)) {
                continue;
            }

            if (in_array($property, $allowed_empty)) {
                continue;
            }

            $missing_options[] = $option;
        }

        return $missing_options;
    }

    /**
     * Ask the user for data if some options are missing.
     */
    private function prompt()
    {
        if (empty($this->db_host)) {
            $this->db_host = $this->ask('What is the database host?', 'localhost');
        }

        if (empty($this->db_port)) {
            $this->db_port = $this->ask('What is the database port?', '3306');
        }

        if (empty($this->db_name)) {
            $this->db_name = $this->ask('What is the database name?');
        }

        if (empty($this->db_username)) {
            $this->db_username = $this->ask('What is the database username?', 'root');
        }

        if (!isset($this->db_password)) {
            $this->db_password = $this->secret('What is the database password?', '');
        }

        if (empty($this->company_name)) {
            $this->company_name = $this->ask('What is the company name?', 'My Company');
        }

        if (empty($this->company_email)) {
            $this->company_email = $this->ask('What is the company contact email?', 'my@company.com');
        }

        if (empty($this->admin_email)) {
            $this->admin_email = $this->ask('What is the admin email?', $this->company_email);
        }

        if (empty($this->admin_password)) {
            $this->admin_password = $this->secret('What is the admin password?');
        }
    }

    private function createDatabaseTables()
    {
        $this->db_host     = $this->option(self::OPT_DB_HOST);
        $this->db_port     = $this->option(self::OPT_DB_PORT);
        $this->db_name     = $this->option(self::OPT_DB_NAME);
        $this->db_username = $this->option(self::OPT_DB_USERNAME);
        $this->db_password = $this->option(self::OPT_DB_PASSWORD);
        $this->db_prefix   = $this->option(self::OPT_DB_PREFIX);

        $this->line('Connecting to database ' . $this->db_name . '@' . $this->db_host . ':' . $this->db_port);

        if (!Installer::createDbTables($this->db_host, $this->db_port, $this->db_name, $this->db_username, $this->db_password, $this->db_prefix)) {
            $this->error('Error: Could not connect to the database! Please, make sure the details are correct.');

            return false;
        }

        return true;
    }
}
