<?php

namespace apexl\Io\modules\install;
use apexl\Config\Singleton;
use apexl\hashing\Hash;
use apexl\Io\includes\System;
use apexl\Io\modules\install\services\databaseTools;
use apexl\Io\modules\install\services\Install;
use apexl\Io\services\pathUtility;

use apexl\Vault\Vault;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class installController {

    /** @var pathUtility  */
    protected $path;
    /** @var Vault  */
    protected $database;
    /** @var Singleton  */
    protected $config;
    /** @var databaseTools */
    protected $dbTools;

    public function __construct(pathUtility $path, Vault $database, Singleton $config, databaseTools $dbTools, Install $install)
    {
        $this->path = $path;
        $this->database = $database;
        $this->config = $config;
        $this->dbTools = $dbTools;
        $this->install = $install;
    }

    /**
     * @param Request $request
     * @param Response $response
     * @method GET
     * @return Response
     */
    public function install(Request $request, Response $response){
        //for now, we return the install options so any system interacting with this can make a choice.
        $options = (object)[
            'installOptions' => (object)[
                'automatic',
            ]
        ];
        //@todo add function to allow for additional install options to be added.
        return System::asJson($response, $options);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     * @method GET
     */
    public function list(Request $request, Response $response){
        //list all modules and their install states
        return System::asJson($response, $list);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @throws \Exception
     * @method POST
     * Automatic install method. Runs the core IO installation process, requires Database user and pass as a minimum to run.
     */
    public function automatic(Request $request, Response $response){
        //@see bodyParserController.php for parsed Json.
        $body = $request->getParsedBody();
        //protect install with a key
        if(!isset($this->config->app->install->key) || empty($this->config->app->install->key)){
            throw new \Exception("There is no configuration Key set in config :: 'app->install->key' Is missing.");
        }

        if(!isset($body->install_key)){
            throw new \Exception("You must provide 'install_key' in the request body");
        }

        if(isset($body->install_key) && $body->install_key == $this->config->app->install->key){
            //check if we have the username / pass for the database already. if we do, skip the update of the config.
            $this->setupConfig($body);
            //loop over the provided databases and install.
            foreach ($body->databases as $key => $values) {
                //Next we need to check if the database exists, if not then we need to create it, or throw an error if there is one.
                $exists = $this->dbTools->checkDBExists($key);
                if (!$exists) {
                    //No DB? that's fine, we need to create it then.
                    $database = $this->database->getDatabase();
                    //Start by unsetting the stored database so its not added to the PDO connection string.
                    $this->database->setDatabase();
                    //use the Db we had stored and attempt to create it.
                    $this->database->createDatabase($database);
                    //no 500's? assume it worked, reset the database and test it again.
                    $this->database->setDatabase($database);
                    $exists = $this->dbTools->checkDBExists($key);
                    //next we check if the new DB exists. If it does, we need to check config for new users or just assign the existing one.
                    if ($exists) {
                        if (isset($body->create_new_database_user_on_install) && $body->create_new_database_user_on_install) {
                            //create a new user / password, overwrite our current known details
                            $user = "Io_user_" . $database . "_" . str_replace(['.', ' '], '', microtime());
                            $pass = (new Hash())->generateRandomHash();
                            $this->database->createUser($user, $pass);
                            $this->database->assignUserToDB($user, $database);

                            //write new config to the config file.
                            System::writeConfig('vault', ['databases' => [$key => ['dbuser' => $user, 'dbpass' => $pass]]]);
                            //reload the config file.
                            (Singleton::getInstance())->reloadConfig();
                        } else {
                            $this->database->assignUserToDB($this->database->getUser(), $database);
                        }
                    }
                }
                $this->install->installModule('install'); //install the install module (namely this is the main site one).
                $this->install->installModule('authentication');
                $this->install->installModule('system');
            }

            return System::asJson($response, ["message" => "Installation Completed."]);
        } else {
            throw new \Exception("The provided Installation key || ".isset($body->install_key) ? $body->install_key : 'Unknown Key'. " || does not match expected key.");
        }
    }

    public function installModule(Request $request, Response $response){
        $body = $request->getParsedBody();
        if(isset($body->install_key) && $body->install_key == $this->config->app->install->key) {
            if (isset($body->modules) && !empty($body->modules)) {
                foreach ($body->modules as $name) {
                    $this->install->installModule($name);
                }
            }
        } else {
            throw new \Exception("The provided Installation key || ".isset($body->install_key) ? $body->install_key : 'Unknown Key'. " || does not match expected key.");
        }
        return System::asJson($response, ["message" => "Installation Completed."]);
    }

    protected function setupConfig($body){
        if(isset($body->databases)) {
            foreach ($body->databases as $key => $values) {
                //first check for config.
                $host = $body->databases->$key->host ?? NULL;
                $port = $body->databases->$key->port ?? NULL;
                $dbname = $body->databases->$key->dbname ?? NULL;
                $driver = $body->databases->$key->driver ?? NULL;
                $operator = $body->databases->$key->operator ?? NULL;
                $user = $body->databases->$key->user ?? NULL;
                $pass = $body->databases->$key->pass ?? NULL;

                //next we check for any missing values and add defaults.
                $config = [
                    'dbhost' => $host ?? 'localhost',
                    'port' => $port ?? '3306',
                    'dbname' => $dbname ?? 'Io_' . $key . '_' . str_replace(['.', ' '], '', microtime()),
                    'driver' => $driver ?? 'mysql',
                    'operator' => $operator ?? 'pdo',
                    'dbuser' => $user ?? '',
                    'dbpass' => $pass ?? '',
                ];

                System::writeConfig('vault', ['databases' => [$key => $config]]);
                (Singleton::getInstance())->reloadConfig();
            }
        }
    }
}