<?php

namespace idoit\Module\Pro\Algorithm\Processor;

use Exception;
use idoit\Module\Pro\Algorithm\Algorithm\TreeAlgorithm;
use idoit\Module\Pro\Algorithm\Filter;
use idoit\Module\Pro\Algorithm\FlatTree;
use idoit\Module\Pro\Algorithm\Formatter\DefaultFormatter;
use idoit\Module\Pro\Algorithm\Visitor\TreeVisitor;
use isys_auth_cmdb_objects;
use isys_cmdb_dao;
use isys_component_database;
use isys_component_template_language_manager;
use isys_exception_general;
use isys_notify;
use isys_tenantsettings;
use isys_tree;
use isys_visualization_profile_model;
use isys_visualization_tree_visitor;

/**
 * Class TreeProcessor
 *
 * @package idoit\Module\Pro\Algorithm\Processor
 */
class TreeProcessor
{
    /**
     * @var isys_component_database
     */
    private $database;

    /**
     * @var isys_component_template_language_manager
     */
    private $language;

    /**
     * @var Filter
     */
    private $filter;

    /**
     * @var array
     */
    private $profile;

    /**
     * TreeProcessor constructor.
     *
     * @param isys_component_database                  $database
     * @param isys_component_template_language_manager $language
     */
    public function __construct(isys_component_database $database, isys_component_template_language_manager $language)
    {
        $this->database = $database;
        $this->language = $language;
    }

    /**
     * @param Filter $filter
     *
     * @return $this
     */
    public function setFilter(Filter $filter): self
    {
        $this->filter = $filter;

        return $this;
    }

    /**
     * @param int $profile
     *
     * @return $this
     * @throws Exception
     */
    public function setProfile(int $profile): self
    {
        $this->profile = isys_visualization_profile_model::instance($this->database)->get_profile_config($profile);

        return $this;
    }

    /**
     * Recursion method for getting all objects.
     *
     * @param int  $objectId
     * @param bool $byMaster
     * @param int  $startCounter
     *
     * @return  isys_tree
     * @throws  isys_exception_general
     */
    public function processAlgorithm(int $objectId, bool $byMaster, int $startCounter): isys_tree
    {
        // Prepare a DAO instance.
        $dao = isys_cmdb_dao::instance($this->database);

        // Prepare a formatter.
        $formatter = new DefaultFormatter($this->database, $this->language);

        // Get new root node for $l_object.
        $rootNode = $formatter->formatNode($objectId, [], false);

        try {
            // Walk through relations and build the tree.
            $treeAlgorithm = new TreeAlgorithm($formatter, $this->filter, $dao);

            // @see  ID-7370  Optionally skip already iterated connections.
            $treeAlgorithm->skipAlreadyIteratedConnections((bool)$this->profile['skip-already-iterated-node']);

            // ID-2896 - Only append the auth-condition, if this feature is enabled.
            if (isys_tenantsettings::get('auth.use-in-cmdb-explorer', false)) {
                $authCondition = str_replace(
                    ['isys_obj__id IN', 'isys_obj__owner_id ='],
                    ['ms.isys_obj__id IN', 'ms.isys_obj__owner_id ='],
                    isys_auth_cmdb_objects::instance()->get_allowed_objects_condition()
                );

                $treeAlgorithm->setAuthCondition($authCondition);
            }

            $treeAlgorithm->iterateNode($rootNode, $this->filter->getLevel(), $byMaster);
        } catch (Exception $e) {
            isys_notify::warning($e->getMessage(), ['sticky' => true]);
        }

        // Modify subnode count.
        $rootNode->set_subnodes($rootNode->count());

        $flatTree = new FlatTree();
        $flatTree->add($rootNode);

        // Filter nodes using the visitor pattern.
        $treeVisitor = new TreeVisitor($this->filter, $this->profile);

        $treeVisitor->setCounter($startCounter);

        while ($treeVisitor->visit($flatTree)) {
            // filtering..
        }

        return $flatTree;
    }
}
