<?php

namespace apexl\Io;

use apexl\Config\Singleton as ConfigSingleton;
use apexl\Io\includes\Hook;
use apexl\Io\includes\System;
use apexl\Io\services\Output;
use Exception;

class Client
{
    public static Client $instance;
    protected string $configDir;
    protected string $configFile;
    protected string $coreConfigPath;
    protected System $system;

    /**
     * Set the primary configuration Dir for IO
     * @param $dir
     * @return $this
     */
    public function setConfigDir($dir): static
    {
        $this->configDir = realpath(rtrim((string) $dir, DIRECTORY_SEPARATOR));
        //check if we have a config cacheDir - if not, create a default one.
        $cacheDir = ConfigSingleton::getCacheDirectory();
        if ($cacheDir) {
            $this->setConfigCacheDir($cacheDir);
        }

        return $this;
    }

    /**
     * Set the directory used to store the amalgamated config files / data as cache.
     * @param $dir
     * @return $this
     */
    public function setConfigCacheDir($dir): Client
    {
        ConfigSingleton::setCacheDirectory($dir);

        return $this;
    }

    /**
     * Set the application base path
     * @param $basePath
     */
    public function setBasePath($basePath): void
    {
        System::setBasePath($basePath);
    }

    /**
     * Bootstrap and run Io
     */
    public function start($configFile): void
    {
        $this->configFile = $configFile;
        //In order to correctly pass cors and output something, we need to make sure we catch any bootstrap errors
        //store them in the systemError output, and continue to try and start the application.
        try {
            $this->bootstrap();
        } catch (Exception $e) {
            error_log(
                sprintf(
                    '[IO Exception :: Start] -%s | %s',
                    $e->getCode(),
                    $e->getMessage()
                ),
                E_USER_WARNING
            );
            Output::addSystemError($e);
        }
        $this->system->start();
    }

    /**
     * Bootstrap IO CMF
     * @throws Exception
     */
    private function bootstrap(): void
    {
        $this->coreConfigPath = sprintf(
            '%s%s%s',
            $this->configDir,
            DIRECTORY_SEPARATOR,
            ltrim(
                $this->configFile,
                DIRECTORY_SEPARATOR
            ),
        );
        ConfigSingleton::load($this->coreConfigPath);

        // Attempt to initialise the System
        $this->system = System::getInstance();
        $this->system->startApplication();
        /** Add a boot hook so modules can set various defaults as soon as the start process is finished*/
        Hook::processHook("boot", ConfigSingleton::getInstance());
    }

    /**
     * Turn Io into a singleton.
     * @return Client
     */
    public static function getInstance(): Client
    {
        if (!isset(self::$instance)) {
            self::$instance = new Client();
        }

        return self::$instance;
    }
}
