<?php declare(strict_types = 1);

namespace idoit\Module\SyneticsFlows\Template;

use idoit\Module\SyneticsFlows\Model\AttributeDataDao;
use idoit\Module\SyneticsFlows\Model\Dto\AttributeData;
use idoit\Module\SyneticsFlows\Model\Dto\ObjectType;
use idoit\Module\SyneticsFlows\Model\ObjectTypeDao;
use idoit\Module\SyneticsFlows\Template\DataVisitor\DataVisitor;
use idoit\Module\SyneticsFlows\Template\TemplateVariables\ObjectAccessTemplate;
use idoit\Module\SyneticsFlows\Template\TemplateVariables\ObjectTypesTemplate;
use idoit\Module\SyneticsFlows\Template\TemplateVariables\PropertyValueTemplate;
use isys_component_template_language_manager;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

class TemplateExtension extends AbstractExtension
{
    private ?AttributeDataDao $dao = null;
    private ?ObjectTypeDao $objectTypeDao = null;

    public function getFilters()
    {
        return [
            new TwigFilter('object', [$this, 'createObjectTemplate', ['is_variadic' => true, 'arguments' => true, 'needs_context' => true]]),
            new TwigFilter('data', [$this, 'getData', ['is_variadic' => true]]),
            new TwigFilter('trans', [$this, 'translate', ['is_variadic' => true]]),
            new TwigFilter('id', [$this, 'getId', ['is_variadic' => true]]),
            new TwigFilter('object_type', [$this, 'getObjectType', ['is_variadic' => true]]),
        ];
    }

    private function getDao(): AttributeDataDao
    {
        if (!$this->dao) {
            $this->dao = new AttributeDataDao(\isys_application::instance()->container->get('database'));
        }
        return $this->dao;
    }

    public function getNodeVisitors()
    {
        return [new DataVisitor()];
    }


    public function getFunctions()
    {
        return [
            new TwigFunction('object', [$this, 'createObjectTemplate']),
            new TwigFunction('data', [$this, 'getData']),
            new TwigFunction('trans', [$this, 'translate']),
            new TwigFunction('object_types', [$this, 'getType']),
            new TwigFunction('object_type', [$this, 'getType']),
        ];
    }

    public function getData($value, string $path = ''): ?AttributeData
    {
        if (!is_string($value) && !is_int($value)) {
            return null;
        }
        if (empty($path)) {
            return null;
        }

        return $this->getDao()->get($path, "$value");
    }

    public function translate($value): string
    {
        if (!is_string($value)) {
            $value = "$value";
        }
        $language = \isys_application::instance()->container->get('language');
        if (!$language instanceof isys_component_template_language_manager) {
            return $value;
        }
        return $language->get($value);
    }

    public function createObjectTemplate($object, array $rest = []): ?ObjectAccessTemplate
    {
        if (!is_string($object) && !is_int($object)) {
            return null;
        }
        return new ObjectAccessTemplate("$object");
    }

    public function getId(mixed $value)
    {
        if ($value instanceof PropertyValueTemplate) {
            return $value->getId();
        }
        return null;
    }

    /**
     * @param string|int $type
     * @return ObjectType|null
     * @throws \isys_exception_database
     */
    public function getType(string|int $type): ?ObjectType
    {
        $types = $this->getObjectTypeDao()->getData();
        foreach ($types as $dataObj) {
            if (is_numeric($type) && (int)$dataObj->getId() === (int)$type) {
                return $dataObj;
            }
            if ($dataObj->getConstant() === $type) {
                return $dataObj;
            }
        }
        return null;
    }

    /**
     * @param string|int $type
     * @param $attr
     * @return mixed
     */
    public function getObjectType(string|int $type, $attr = null): mixed
    {
        $path = array_filter([$type, $attr]);
        return (new ObjectTypesTemplate())->{join('.', $path)};
    }

    /**
     * @return ObjectTypeDao
     * @throws \Exception
     */
    public function getObjectTypeDao(): ObjectTypeDao
    {
        if (!$this->objectTypeDao) {
            $this->objectTypeDao = \isys_application::instance()->container->get('idoit.flows.object-type.dao');
        }
        return $this->objectTypeDao;
    }
}