<?php declare(strict_types = 1);

namespace idoit\Module\SyneticsFlows\Template\TemplateVariables;

use idoit\Module\SyneticsFlows\Model\CategoryDao;
use isys_application;
use isys_auth;
use isys_auth_cmdb_objects;
use isys_cmdb_dao_category;
use isys_cmdb_dao_category_data_multivalue;
use isys_cmdb_dao_category_data_reference;

class CategoryDataTemplate implements \IteratorAggregate
{
    private ?array $data = null;

    private ?isys_cmdb_dao_category $dao = null;

    public function __construct(private string $constant, private string $objectId)
    {
    }

    private function getDao(): ?isys_cmdb_dao_category
    {
        if ($this->dao === null) {
            $dao = new CategoryDao(isys_application::instance()->container->get('database'));
            $this->dao = $dao->getDaoInstance($this->constant);
        }
        return $this->dao;
    }

    private function resolveCategoryData(): void
    {
        if ($this->data !== null) {
            return;
        }

        $dao = $this->getDao();

        if (!$dao || !isys_auth_cmdb_objects::instance()->is_allowed_to(isys_auth::VIEW, "OBJ_ID/{$this->objectId}")) {
            return;
        }

        $this->data = [];

        try {
            $id = $dao->get_object_id();
            $properties = $dao->get_properties();
            $propertyMap = [];

            foreach ($properties as $property => $data) {
                $propertyMap[$property] = $data['data']['field'];
            }
            $dao->set_object_id($this->objectId);

            $results = new \isys_array();
            $results = $dao->category_data($results);
            foreach ($results as $id => $result) {
                $data = [];
                $rawData = $dao->get_data($id)->get_row() ?? [];
                foreach ($result as $key => $value) {
                    $data[$key] = new PropertyValueTemplate(
                        $value,
                        $this->prepareRawData($key, $value, $rawData, $propertyMap),
                    );
                }
                $this->data[] = $data;
            }
        } finally {
            $dao->set_object_id($id);
        }
    }

    /**
     * @param string $property
     * @param mixed $value
     * @param array $rawData
     * @param array $propertyMap
     * @return mixed
     */
    private function prepareRawData(string $property, mixed $value, array $rawData, array $propertyMap): mixed
    {
        if ($value instanceof isys_cmdb_dao_category_data_multivalue) {
            return array_map(fn(mixed $val) => $val->m_id, $value->m_value);
        }

        if ($value instanceof isys_cmdb_dao_category_data_reference) {
            return $value->m_id;
        }

        if (isset($propertyMap[$property])) {
            return $rawData[$propertyMap[$property]] ?? null;
        }

        return null;
    }

    public function at(int $index): ?array
    {
        $this->resolveCategoryData();
        return $this->data[$index] ?? null;
    }

    public function __get(string|int $index)
    {
        $this->resolveCategoryData();
        if (is_string($index) && !is_numeric($index)) {
            return $this->data[0][$index] ?? null;
        }
        if (isset($this->data[$index])) {
            return $this->data[$index];
        }

        return null;
    }

    public function __isset(string|int $index)
    {
        $this->resolveCategoryData();
        if (is_string($index) && !is_numeric($index)) {
            return isset($this->data[0][$index]);
        }
        return isset($this->data[$index]);
    }

    public function __toString(): string
    {
        $this->resolveCategoryData();
        return json_encode($this->data, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
    }

    public function getIterator()
    {
        $this->resolveCategoryData();
        return new \ArrayIterator($this->data ?? []);
    }
}
