<?php

namespace idoit\Module\Pro\Controller\CmdbExplorer;

use idoit\Module\Pro\Algorithm\Filter;
use idoit\Module\Pro\Algorithm\Processor\TreeProcessor;
use idoit\Module\Pro\Algorithm\Visitor\TreeVisitor;
use isys_application;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Throwable;

/**
 * Tree controller for CMDB-Explorer.
 *
 * @package   Modules
 * @copyright synetics GmbH
 * @license   http://www.i-doit.com/license
 */
class Tree
{
    private \isys_component_database $database;
    private \isys_component_template_language_manager $language;
    private \isys_cmdb_dao $cmdbDao;

    public function __construct()
    {
        $this->database = isys_application::instance()->container->get('database');
        $this->language = isys_application::instance()->container->get('language');
        $this->cmdbDao = isys_application::instance()->container->get('cmdb_dao');
    }

    /**
     * @param Request $request
     *
     * @return JsonResponse
     */
    public function loadLevel(Request $request): JsonResponse
    {
        try {
            \isys_module_cmdb::getAuth()->check(\isys_auth::VIEW, 'EXPLORER');

            $objectId = (int)$request->query->get('object');
            $profileId = (int)$request->query->get('profile');
            $byMaster = (bool)$request->query->get('by_master');
            $lastId = (int)$request->query->get('last_id');
            $filter = $this->prepareFilter((int)$request->query->get('filter'));
            $treeProcessor = $this->getTreeProcessor($filter, $profileId);

            $treeProcessor->setFilter($filter->setLevel(1));

            $responseData = [
                'success' => true,
                'data' => [
                    'filterWarning' => $filter->getFilterWarning(),
                    'nodes' => $treeProcessor->processAlgorithm($objectId, $byMaster, ($lastId + 10)),
                    'count' => TreeVisitor::getCounter()
                ],
                'message' => ''
            ];

            return new JsonResponse($responseData);
        } catch (Throwable $e) {
            return new JsonResponse([
                'success' => false,
                'data' => null,
                'message' => $e->getMessage()
            ]);
        }
    }

    /**
     * @param Request $request
     *
     * @return JsonResponse
     */
    public function loadData(Request $request): JsonResponse
    {
        try {
            \isys_module_cmdb::getAuth()->check(\isys_auth::VIEW, 'EXPLORER');

            $objectId = (int)$request->query->get('object');
            $profileId = (int)$request->query->get('profile');
            $filter = $this->prepareFilter((int)$request->query->get('filter'));
            $treeProcessor = $this->getTreeProcessor($filter, $profileId);

            // @see  ID-7223  Check if the object exists, before processing the rest.
            if (!$this->cmdbDao->obj_exists($objectId)) {
                throw new \Exception($this->language->get('LC__CMDB__OBJECT_DOES_NOT_EXIST', $objectId));
            }

            $explorerA = $treeProcessor->processAlgorithm($objectId, true, 0)->toArray();
            $explorerB = $treeProcessor->processAlgorithm($objectId, false, 0)->toArray();

            // We use the visitor for the "last inserted ID".
            $responseData = [
                'success' => true,
                'data' => [
                    'limitWarning'     => $filter->getFilterWarning(),
                    'explorer_a'       => $explorerA,
                    'explorer_a_count' => \count($explorerA),
                    'explorer_b'       => $explorerB,
                    'explorer_b_count' => \count($explorerB),
                    'profile'          => \isys_visualization_profile_model::instance($this->database)->get_profile_config($profileId)
                ],
                'message' => ''
            ];

            return new JsonResponse($responseData);
        } catch (Throwable $e) {
            return new JsonResponse([
                'success' => false,
                'data' => null,
                'message' => $e->getMessage()
            ]);
        }
    }

    /**
     * @param Filter $filter
     * @param int    $profileId
     *
     * @return TreeProcessor
     * @throws \Exception
     */
    private function getTreeProcessor(Filter $filter, int $profileId): TreeProcessor
    {
        return (new TreeProcessor($this->database, $this->language))
            ->setProfile($profileId)
            ->setFilter($filter);
    }

    /**
     * @param int $filterId
     *
     * @return Filter
     * @throws \Exception
     */
    private function prepareFilter(int $filterId): Filter
    {
        $filterData = [];
        $filter = new Filter();

        if ($filterId > 0) {
            $filterData = \isys_itservice_dao_filter_config::instance($this->database)->get_data($filterId)['formatted__data'];
        }

        if (!isset($filterData['level']) || empty($filterData['level'])) {
            // @see  ID-8807  Limit the recursion run to something low, so we don't run into memory issues.
            $filterData['level'] = 2;
            $filter->setFilterWarning(true);
        }

        if (is_numeric($filterData['priority'])) {
            $filter->setPriority((int)$filterData['priority']);
        }

        if (is_numeric($filterData['level'])) {
            $filter->setLevel((int)$filterData['level']);
        }

        if (is_array($filterData['object-type']) && count($filterData['object-type'])) {
            $filter->setObjectTypes($filterData['object-type']);
        }

        if (is_array($filterData['relation-type']) && count($filterData['relation-type'])) {
            $filter->setRelationTypes($filterData['relation-type']);
        }

        if (is_array($filterData['cmdb-status']) && count($filterData['cmdb-status'])) {
            $filter->setCmdbStatus($filterData['cmdb-status']);
        }

        return $filter;
    }
}
