<?php declare(strict_types = 1);

namespace idoit\Module\SyneticsFlows\Model;

use idoit\Component\Property\Property;
use idoit\Model\Dao\Base;
use idoit\Module\Cmdb\Component\AttributeDataCollector\Collector;
use idoit\Module\Multiedit\Component\Multiedit\Exception\CategoryDataException;
use idoit\Module\SyneticsFlows\Controller\SearchParams;
use idoit\Module\SyneticsFlows\Filter\AttributeDataFilter;
use idoit\Module\SyneticsFlows\Model\AttributeDataExtractor\Extractor;
use idoit\Module\SyneticsFlows\Model\Dto\AttributeData;
use isys_application;
use isys_cmdb_dao_category;
use isys_cmdb_dao_category_g_custom_fields;
use Throwable;

class AttributeDataDao extends Base
{
    private ?Extractor $extractor = null;

    /**
     * @return Extractor
     * @throws \Exception
     */
    private function getExtractor(): Extractor
    {
        if ($this->extractor === null) {
            $this->extractor = Extractor::factory();
        }

        return $this->extractor;
    }

    /**
     * @return int
     */
    public function getCount(AttributeDataFilter $filter): int
    {
        return count($this->getData($filter));
    }

    /**
     * @param string $attribute
     * @param string $categoryConst
     * @param string|null $entryId
     *
     * @return array
     * @throws CategoryDataException
     * @throws \isys_exception_database
     */
    private function process(string $attribute, string $categoryConst, ?string $entryId = null): array
    {
        $dataCollector = new Collector();
        $categoryDao = new CategoryDao(isys_application::instance()->container->get('database'));
        $categoryInstance = $categoryDao->getDaoInstance($categoryConst);

        if (!$categoryInstance) {
            return [];
        }

        $properties = $categoryInstance->get_properties();
        $extractor = $this->getExtractor();

        $property = $properties[$attribute] ?? null;

        if (!$property) {
            return [];
        }

        if (!$property instanceof Property) {
            $property = Property::createInstanceFromArray($property);
        }

        if (!$dataCollector->isApplicable($property)) {
            return [];
        }

        try {
            $dataCollectorType = $dataCollector->getApplicableType();
            if (!$extractor->supports($dataCollectorType)) {
                return [];
            }

            return $extractor->extract($property, $dataCollectorType, $entryId);
        } catch (Throwable $e) {
            // do nothing
        }

        return [];
    }

    /**
     * @param AttributeDataFilter $filter
     * @param SearchParams|null $params
     *
     * @return AttributeData[]
     * @throws CategoryDataException
     * @throws \isys_exception_database
     */
    public function getData(AttributeDataFilter $filter, ?SearchParams $params = null): array
    {
        $attribute = $filter->getAttribute();
        $entryId = $filter->getEntryId();
        $categoryConst = $filter->getCategoryFilter()->getCategories()[0];

        return $this->process($attribute, $categoryConst, $entryId);
    }

    /**
     * @param string $id
     * @param string $entryId
     *
     * @return AttributeData|null
     *
     * @throws CategoryDataException
     * @throws \isys_exception_database
     */
    public function get(string $id, string $entryId): ?AttributeData
    {
        $data = $this->getData(AttributeDataFilter::factory($id, $entryId));

        if (empty($data)) {
            return null;
        }

        return current($data);
    }
}
