<?php

namespace Snaju\Inception\Plugin;

use Composer\InstalledVersions;
use Snaju\Inception\Exception\Exception;
use Snaju\Inception\Exception\PluginNotInstalledException;
use Snaju\Inception\InceptionCore;
use Snaju\Inception\ORM\ORM;
use Snaju\Inception\Route\Loaders\Twig\Template;
use Snaju\Inception\Route\Loaders\Twig\TemplateManager;
use Snaju\Inception\Route\Loaders\Twig\TwigRoute;
use Snaju\Inception\Route\RouteManager;
use Snaju\Inception\Settings\Config;
use Snaju\Inception\Util\Loader;
use Snaju\Inception\Util\Util;

class PluginManager
{

    public static $plugins = [];

    public static $loaded = [];

    private static $loc = "plugins";

    public static function init($loc = "plugins")
    {
        self::$loc = $loc;

        foreach (scandir(InceptionCore::getBase() . "/" . self::$loc) as $pluginDir) {

            $fullPath = InceptionCore::getBase() . "/" . self::$loc . "/" . $pluginDir;

            if (is_dir($fullPath) && ($fullPath != "." && $fullPath != "..")) {

                // Is a plugin dir if it contains a plugin.php file
                if (file_exists($fullPath . "/" . "plugin.json")) {
                    // A plugin.json file exist.

                    new Loader($fullPath, '/.*\.php/mi');

                    $config = json_decode(file_get_contents($fullPath . "/" . "plugin.json"));

                    if (isset($config->main) && isset($config->depend) && isset($config->composer)) {
                        // Has a valid config

                        $main = new \ReflectionClass($config->main);
                        $o = $main->newInstance();
                        if ($o instanceof PluginInterface) {
                            $pluginObj = new PluginObject($o, $fullPath);
                            if (isset($config->composer)) {
                                $pluginObj->setComposer($config->composer);
                            }
                            if (isset($config->depend)) {
                                $pluginObj->setDepends($config->depend);
                            }
                            if (isset($config->autoload)) {
                                $pluginObj->setAutoload($config->autoload);
                            }

                            /*
                             * Set the config obj.
                             * */
                            $cObj = new \ReflectionClass(PluginConfig::class);
                            $pc = new PluginConfig();
                            foreach ($config as $k => $v) {
                                if ($cObj->hasProperty($k)) {
                                    $pc->{$k} = $v;
                                }
                            }

                            /*
                             * Check for certain config to be set.
                             * */
                            if ($pc->views == null && file_exists($fullPath . "/" . "views")) {
                                $pc->views = "views";
                            }

                            if ($pc->routes == null && file_exists($fullPath . "/" . "routes")) {
                                $pc->routes = "routes";
                            }

                            if ($pc->templates == null && file_exists($fullPath . "/" . "templates")) {
                                $pc->templates = "templates";
                            }


                            $pluginObj->setConfig($pc);

                            if (isset(self::$plugins[$config->main])) {
                                throw new Exception("Plugin main already loaded...");
                            } else {
                                self::$plugins[$config->main] = $pluginObj;
                            }
                        } else {
                            throw new Exception("Plugin Main Class must impliment PluginInterface");
                        }
                    }
                }
            }
        }

    }

    public static function isInstalled(string $simpleName)
    {
        foreach (self::$plugins as $className => $obj) {
            if ($obj instanceof PluginObject) {
                if (strtolower($className) == strtolower($simpleName)) {
                    return true;
                } else {
                    // Check the simple name.
                    if (strtolower($obj->getPlugin()->getSimpleName()) == strtolower($simpleName)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public static function get(string $simpleName): PluginObject
    {
        foreach (self::$plugins as $className => $obj) {
            if ($obj instanceof PluginObject) {
                if (strtolower($className) == strtolower($simpleName)) {
                    require $obj;
                } else {
                    // Check the simple name.
                    if (strtolower($obj->getPlugin()->getSimpleName()) == strtolower($simpleName)) {
                        require $obj;
                    }
                }
            }
        }

        require null;
    }

    public static function loadIfNotAlready(PluginObject $pluginObj)
    {
        if ($pluginObj->isAutoload() && !isset(self::$loaded[$pluginObj->getDir()])) {
            /*
             * Check the depends.
             * */
            foreach ($pluginObj->getDepends() as $depend) {
                if (!self::isInstalled($depend)) {
                    throw new PluginNotInstalledException($depend, $pluginObj->getPlugin()->getSimpleName());
                }

                // Is there.
                self::loadIfNotAlready(self::get($depend));
            }

            $pluginObj->getPlugin()->onEnable();

            // Load any of the config items.
            if ($pluginObj->getConfig() != null) {
                $config = $pluginObj->getConfig();
                if ($config instanceof PluginConfig) {
                    // Is a valid config item.

                    if ($config->routes != null) {
                        // Load any routes from the /routes folder
//                        $loader = new Loader($pluginObj->getDir() . "/routes", '/.*?\.php/mi', true);
//                        foreach ($loader->getMatches() as $match) {
//                            // Load the routes here.
//                            RouteManager::init();
//                        }

                        // TODO: See if we need this, possible we don't and it will load with the PHP Doc Tags

                    }
                    if ($config->templates != null) {
                        // Load any templates from the /templates folder.

                        /*
                         * Load into the view system.
                         * */
                        $loader = new Loader($pluginObj->getDir() . "/" . $config->templates, '/.*\.(twig|html)/mix', false);

                        foreach ($loader->getMatches() as $matchLoc) {
                            preg_match('/(.*)\.(twig|html)/mi', $matchLoc, $m);

                            // remove the base from the file system.
                            $relative = str_replace(array($pluginObj->getDir() . "/" . "views"), "", $m[1]);
                            $relative = ltrim($relative, '/');

                            $template = new Template($relative, $matchLoc);
                            TemplateManager::register($template);
                        }

                    }
                    if ($config->views != null) {
                        // Load any views from the /views folder.

                        /*
                         * Load into the view system and then make a route.
                         * */
                        $loader = new Loader($pluginObj->getDir() . "/" . $config->views, '/.*\.(twig|html)/mix', false);

                        foreach ($loader->getMatches() as $matchLoc) {
                            preg_match('/(.*)\.(twig|html)/mi', $matchLoc, $m);

                            // remove the base from the file system.
                            $relative = str_replace(array($pluginObj->getDir() . "/" . "views"), "", $m[1]);
                            $relative = ltrim($relative, '/');

                            $template = new Template($relative, $matchLoc);
                            TemplateManager::register($template);

                            RouteManager::register(new TwigRoute($matchLoc, $relative));
                        }

                    }
                    if (file_exists($pluginObj->getDir() . "/" . "require")) {
//                        new Loader($pluginObj->getDir() . "/" . "require", '/.*?\.php/mi');
                    }

                    if (file_exists($pluginObj->getDir() . "/" . "model")) {
                        ORM::addModelLocation($pluginObj->getDir() . "/" . "model");
                    }
                }
            }

            /*
             * Set the plugin as installed.
             * */
            self::$loaded[$pluginObj->getDir()] = $pluginObj;

        }
    }

    public static function composerPreInstallHook()
    {
        // pre-install-cmd

        $composerDepends = [];

        $composerLoc = InceptionCore::getBase() . "/composer.json";

        if (file_exists($composerLoc)) {
            // Load the composer current json.
            $composerJson = json_decode(file_get_contents($composerLoc));

            foreach (self::$plugins as $plugin) {
                if ($plugin instanceof PluginObject) {
                    foreach ($plugin->getComposer() as $composerPackage => $version) {
                        if (!InstalledVersions::isInstalled($composerPackage)) {
                            // Package is not installed
                            $composerJson['requires'][$composerPackage] = $version;
                        }
                    }
                }
            }

            file_put_contents($composerLoc, json_encode($composerJson, JSON_PRETTY_PRINT));
        }
    }

}