<?php

namespace Snaju\Inception\Route\Loaders\Twig;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Snaju\Inception\Exception\ViewNotFoundException;
use Snaju\Inception\ORM\DataModel;
use Snaju\Inception\ORM\DataModelProcessor;
use Snaju\Inception\Route\Loaders\Twig\Extensions\Async\AsyncNodeVisitor;
use Snaju\Inception\Route\Loaders\Twig\Extensions\Async\AsyncTwigExtention;
use Snaju\Inception\Route\Loaders\Twig\Extensions\CustomFunctions;
use Snaju\Inception\Util\ArrayUtil;
use Snaju\Inception\Util\Loaders\Queue;
use Snaju\Inception\Util\Loaders\Stack;
use Snaju\Inception\Util\Util;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;

class TemplateManager
{

    public static string $defaultTemplateDir = __DIR__ . '/DefaultTemplates/';
    public static array $templates = [];
    public static ?Environment $env = null;
    public static $context;

    public static array $stack = [];

    public static array $controllers = [];

    // BUG: template scopes working, but data not loading from different scopes in twig templates
    public static function addData($key, $value, $writeover = false)
    {
        if (self::$context == null) {
            self::$context = [];
        }

        $keys = explode(".", $key);

        $pointer = &self::$context;
        foreach ($keys as $k) {
            $pointer = &$pointer[$k];
        }

        if ($pointer == null) {
            $pointer = $value;
        }
    }

    public static function getData(string $path)
    {
        $pointer = &self::$context;
        $keys = explode(".", $path);
        foreach ($keys as $k) {
            if (!isset($pointer->{$k})) {
                $pointer = &$pointer->{$k};
            } else {
                return null;
            }
        }

        return $pointer;
    }

    public static function getAll()
    {
        return self::$context;
    }

    public static function merge(array $data)
    {
        self::$context = array_merge_recursive(self::$context, $data);
    }

    public static function addMany(array $arr)
    {
        $oneD = ArrayUtil::getDotArray($arr);
        foreach ($oneD as $k => $v) {
            self::addData($k, $v);
        }
    }

    private
    static function isPath($key)
    {
        try {
            return (strpos($key, ".") !== false);
        } catch (\Exception $exception) {
            return false;
        }
    }

    /**
     * Set twig environment in template manager and loads custom extensions.
     * @param Environment $env
     */
    public static function setTwigEnv(Environment $env)
    {
        self::$env = $env;

        // Load custom extensions (globals, functions, filters, etc.)
        self::$env->addExtension(new CustomFunctions());
    }

    public static function register(Template $template)
    {
        self::$templates[$template->getId()] = $template;
    }

    public static function exist($id): bool
    {
        return (bool)(isset(self::$templates[$id]));
    }

    public static function render(string $templateId, array $data = [], Environment $env = null)
    {
        if ($env == null && self::$env instanceof Environment) {
            $env = self::$env;
        }

        if (self::exist($templateId)) {

            if (count($data) > 0) {
                $data = array_merge((array)self::$context, $data);
            }

//            $env->render($templateId, array_merge(self::$scope[$scope], $data));
            return $env->render($templateId, (count($data) > 0) ? $data : (array)self::$context);
        }

        // If the template isn't found, throw 404 view not found exception
        throw new ViewNotFoundException('Invalid Template ID', 404);
    }

    public static function renderDefaultTemplate($templateId, $data = []): void
    {
        $loader = new FilesystemLoader(self::$defaultTemplateDir);
        $twigEnv = new Environment($loader);
        $file = self::$defaultTemplateDir . $templateId . '.twig';

        if (file_exists($file)) {
            $template = $twigEnv->load($templateId . '.twig');
            echo $template->render($data);
        } else {
            // TODO: handle exception within an exception
        }
    }

    public static function dataModelObjectsToTemplateObjects(array &$objs)
    {
        foreach ($objs as &$obj) {
            if ($obj instanceof DataModel) {
                $obj->toTemplateObject();
            }
        }

        return $objs;
    }

}