<?php

namespace idoit\Module\SyneticsFlows\Automation\Trigger\ConditionBuilder;

use idoit\Module\SyneticsFlows\Dto\Criteria;
use isys_array;
use isys_cmdb_dao_category_property;
use isys_component_database;
use ReflectionClass;
use Throwable;

class CmdbConditionBuilder implements ConditionBuilder
{
    public function __construct(
        private isys_component_database $database
    )
    {
    }

    /**
     * @param string $field
     * @param Criteria $criteria
     *
     * @return string
     * @throws ConditionBuildException
     */
    public function buildSqlCriteria(string $field, Criteria $criteria): string
    {
        if ($criteria instanceof Criteria\QueryCriteria) {
            $child = $criteria->getCriteria();
            $alias = uniqid('object');
            $subCriteria = $this->buildSqlCriteria("$alias.isys_obj__id", $child);
            $idsQuery = "(SELECT $alias.isys_obj__id FROM isys_obj $alias WHERE TRUE AND $subCriteria)";

            $condition = $criteria->prepareReportManagerCondition();
            $sql = $this->getObjectIdSqlCondition($field, $condition);
            $placeholder = Criteria\QueryCriteria::PLACEHOLDER;
            return str_replace("'$placeholder'", $idsQuery, $sql);
        }

        if ($criteria instanceof Criteria\FieldCriteria) {
            return $this->getObjectIdSqlCondition($field, $criteria->prepareReportManagerCondition());
        }

        if ($criteria instanceof Criteria\AggregateCriteria) {
            $items = array_map(fn (Criteria $child) => $this->buildSqlCriteria($field, $child), $criteria->getItems());
            $operator = $criteria->getOperator();
            return join(" $operator ", $items);
        }

        return 'FALSE';
    }

    private function getObjectIdSqlCondition(string $field, array $conditions): string
    {
        try {
            $queryBuilder = new isys_cmdb_dao_category_property($this->database);
            $queryBuilder->set_query_as_report(true)
                ->reset();

            $queryConditions = $queryBuilder->create_property_query_condition([$conditions]);
            $joins = array_filter((new isys_array($queryConditions['joins']))->flatten());

            $class = new ReflectionClass($queryBuilder);
            $attribute = $class->getProperty('m_sub_joins');
            $extraJoins = $attribute->getValue($queryBuilder);

            if ($extraJoins) {
                $joins = array_merge($joins, array_filter((new isys_array($extraJoins))->flatten()));
            }

            $alias = uniqid('object');
            $sql = sprintf('SELECT obj_main.isys_obj__id FROM isys_obj as obj_main %s WHERE TRUE %s', implode(' ', $joins), $queryConditions['conditions']);
            $sql = str_replace("obj_main", $alias, $sql);

            return "$field IN ($sql)";
        } catch (Throwable $e) {
            throw ConditionBuildException::buildError('CMDB', $e->getMessage());
        }
    }
}
