<?php

/**
 * @TODO write exception class for vault, add better error checking with meaningful errors.
 */
namespace apexl\Vault;
use apexl\Config\Singleton;
use apexl\Vault\interfaces\driver;

/**
 * Class databaseConnection
 * PHP Class to manage all database connections and queries. Designed to be secure and efficient
 * @Author Toby New (toby@ubyk.co.uk)
 */
class Vault
{
    protected $config;
    protected $vaults = [];
    protected $manualConfig = [];
    protected $defaultVault = 'default';
    protected $initialised = FALSE;
    protected $driver;
    protected $loggedInUserId;
    private static $instance;

    /**
     * databaseConnection constructor.
     * @param $database
     * @param $user
     * @param $password
     * @param string $host
     * @param array $options
     */
    private function __construct()
    {
        //reference the config singleton
        $this->config = Singleton::getInstance();
        //if we have the vault config set, then allow us to initialise it.
        if(isset($this->config->vault)){
            //initiate vault classes.
            $this->initiateVaults();
        }
    }

    /**
     * @todo maybe combine some of the other methods that make this up into one place?
     * @param $dbName
     * @return mixed
     */
    public function testConnection($dbName = NULL){
        $testDb = !$dbName ? $this->defaultVault : $dbName;
        return $this->{$testDb}->testConnection();
    }

    /**
     * Loop over the provided database configurations and load the appropriate drivers.
     * This is a public method so it can be invoked manually if config only becomes available after instantiation.
     */
    public function initiateVaults(){
        if(!isset($this->config->vault)){
            throw new \Exception('"vault" key missing from loaded configuration');
        }
        if(isset($this->config->vault->databases) && !empty($this->config->vault->databases)) {
            foreach ($this->config->vault->databases as $dbName => $dbConfig) {
                $this->driver = isset($dbConfig->driver) && !empty($dbConfig->driver) ? "\\apexl\\Vault\\operators\\drivers\\" . $dbConfig->driver : "\\apexl\\Vault\\operators\\drivers\\mysql";
                $this->vaults[$dbName] = $this->instantiateDriver(new $this->driver($dbConfig));
            }
        }
        $this->initialised = TRUE;
    }

    public function initiateVault($vaultName){
        if(!isset($this->config->vault)){
            throw new \Exception('"vault" key missing from loaded configuration');
        }
        if(isset($this->config->vault->databases->{$vaultName})){
            $this->driver = isset($dbConfig->driver) && !empty($dbConfig->driver) ? "\\apexl\\Vault\\operators\\drivers\\".$dbConfig->driver : "\\apexl\\Vault\\operators\\drivers\\mysql";
            $this->vaults[$vaultName] = $this->instantiateDriver(new $this->driver($this->config->vault->databases->{$vaultName}));
        }
        $this->initialised = TRUE;
    }

    //add setters for manual config.
    public function setDriver($driver = ''){
        $this->driver = isset($driver) && !empty($driver) ? "\\apexl\\Vault\\operators\\drivers\\".$driver : "\\apexl\\Vault\\operators\\drivers\\mysql";
        $this->manualConfig['driver'] = $driver;
        return $this;
    }

    /**
     * This method allows us to force the provided driver to use the correct interface
     * We also use it to set the logged in userId, if known at this point
     * @param driver $driver
     * @return driver
     */
    private function instantiateDriver(driver $driver){
        if ($this->loggedInUserId) {
            $driver->setLoggedInUserId($this->loggedInUserId);
        }

        return $driver;
    }

    public static function getInstance(){
        if(!self::$instance){
            self::$instance = new Vault();
        }
        return self::$instance;
    }

    public function __get($name){
        return isset($this->vaults[$name]) ? $this->vaults[$name] : FALSE;
    }

    public function getDefaultVault(){
        return $this->defaultVault;
    }

    public function setDefaultVault($name){
        if(!isset($this->vaults[$name])){
            throw new \Exception($name.' missing from loaded configuration');
        }
        $this->defaultVault = $name;
    }

    public function isInitialised(){
        return $this->initialised;
    }

    /**
     * @param $name
     * @param $arguments
     * @return bool
     */
    public function __call($name, $arguments)
    {
        return isset($this->vaults[$this->defaultVault]) ? $this->vaults[$this->defaultVault]->$name(...$arguments) : FALSE;
    }

    public function __isset($name){
        return isset($this->vaults[$name]);
    }

    public function __unset($name)
    {
        unset($this->vaults[$name]);
    }

    public function __destruct()
    {
        $this->vaults = null;
    }

    public function setLoggedInUserId(int $userId)
    {
        $this->loggedInUserId = $userId;
        foreach ($this->vaults as $vaultName => $driver) {
            $this->vaults[$vaultName]->setLoggedInUserId($userId);
        }
    }
}