<?php

namespace idoit\Module\Cmdb\Search\Index;

use idoit\Component\Provider\Singleton;
use idoit\Module\Cmdb\Search\Index\Data\CategoryCollector;
use idoit\Module\Search\Index\Engine\Mysql;
use idoit\Module\Search\Index\Manager;
use isys_application;
use isys_component_signalcollection as SignalCollection;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * i-doit
 *
 * Signal manager for search index signals
 *
 * @package     i-doit
 * @subpackage  Cmdb
 * @author      Dennis Stücken <dstuecken@i-doit.com>
 * @version     1.7
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 */
class Signals
{
    use Singleton;

    /**
     * @var int
     */
    private $startTime;

    /**
     * @var Manager
     */
    private $manager;

    /**
     * Default output is BufferedOutput
     *
     * @var OutputInterface
     */
    private $output;

    /**
     * Disconnect afterCategoryEntrySave Event
     *
     * @throws \Exception
     */
    public function disconnectOnAfterCategoryEntrySave()
    {
        SignalCollection::get_instance()
            ->disconnect('mod.cmdb.afterCategoryEntrySave', [
                $this,
                'onAfterCategoryEntrySave'
            ]);
    }

    public function onBeforeCategoryEntrySave(\isys_cmdb_dao_category $dao, $categoryID, $objectID, $posts, $changes)
    {
        if (isset($changes['C__OBJ__TYPE']) && $changes['C__OBJ__TYPE']['from'] != $changes['C__OBJ__TYPE']['to']) {
            /**
             * @var $collector CategoryCollector
             */
            $collector = $this->manager->getCollector('idoit.cmdb.search.index.category_collector');
            $collector->setCategoryConstants([$dao->get_category_const()]);
            $collector->setObjectIds([$objectID]);

            $this->manager->setMode(Manager::MODE_DELETE);
            $this->manager->setOutput($this->output);
            $this->manager->generateIndex();
        }
    }

    /**
     * @param \isys_cmdb_dao_category $dao
     * @param                         $categoryID
     * @param                         $saveSuccess
     * @param                         $objectID
     * @param                         $posts
     * @param                         $changes
     */
    public function onAfterCategoryEntrySave(\isys_cmdb_dao_category $dao, $categoryID, $saveSuccess, $objectID, $posts, $changes)
    {
        /**
         * @var $collector CategoryCollector
         */
        $collector = $this->manager->getCollector('idoit.cmdb.search.index.category_collector');
        $collector->setCategoryConstants([$dao->get_category_const()]);
        $collector->setObjectIds([$objectID]);

        $this->manager->setMode(Manager::MODE_OVERWRITE);
        $this->manager->setOutput($this->output);
        $this->manager->generateIndex();
    }

    /**
     * AfterCsvImport::Event
     *
     * @param $modCsvImport
     * @param $transformedData
     * @param $createdObjects
     * @param $categoryMap
     * @param $updatedCategories
     *
     * @throws \Exception
     */
    public function onAfterCsvImport($modCsvImport, $transformedData, $createdObjects, $categoryMap, $updatedCategories)
    {
        // Use default postImport::Event to update the search index
        $this->onPostImport($this->startTime, $updatedCategories);
    }

    /**
     * PostImport::Event
     *
     * @param       $importStartTime
     * @param array $importedGlobalCategories
     * @param array $importedSpecific
     *
     * @throws \Exception
     */
    public function onPostImport($importStartTime, $importedGlobalCategories = [], $importedSpecific = [] /*, $importType = '', $rawData = []*/)
    {
        if ($importStartTime > 0) {
            // Retrieve changed objects
            $l_changed_objects = [];
            $l_objects = \isys_cmdb_dao_nexgen::instance(\isys_application::instance()->container->get('database'))
                ->get_objects([
                    'changed_after' => $importStartTime - 180
                ]);
            while ($l_row = $l_objects->get_row()) {
                $l_changed_objects[] = $l_row['isys_obj__id'];
            }

            /**
             * @var $collector CategoryCollector
             */
            $collector = $this->manager->getCollector('idoit.cmdb.search.index.category_collector');
            $collector->setCategoryConstants(array_merge($importedGlobalCategories, $importedSpecific));
            $collector->setObjectIds($l_changed_objects);

            $this->manager->setMode(Manager::MODE_OVERWRITE);
            $this->manager->setOutput($this->output);
            $this->manager->generateIndex();
        }
    }

    /**
     * @param \isys_cmdb_dao_category $dao
     * @param array                   $rawData
     * @param array                   $changes
     */
    public function onMultiEditSaved(\isys_cmdb_dao_category $dao, $rawData, $changes)
    {
        /**
         * @var $collector CategoryCollector
         */
        $collector = $this->manager->getCollector('idoit.cmdb.search.index.category_collector');
        $collector->setCategoryConstants([$dao->get_category_const()]);
        $collector->setObjectIds(array_keys($changes));

        $this->manager->setMode(Manager::MODE_OVERWRITE);
        $this->manager->setOutput($this->output);
        $this->manager->generateIndex();
    }

    /**
     * @param array                     $objects
     * @param int                       $templateID
     * @param \isys_import_handler_cmdb $importHandler
     */
    public function onMassChangeApplied(array $objects, $templateID, \isys_import_handler_cmdb $importHandler)
    {
        /**
         * @var $collector CategoryCollector
         */
        $collector = $this->manager->getCollector('idoit.cmdb.search.index.category_collector');
        $collector->setObjectIds(array_keys($objects));

        $this->manager->setMode(Manager::MODE_OVERWRITE);
        $this->manager->setOutput($this->output);
        $this->manager->generateIndex();
    }

    /**
     * @param int            $p_objectID
     * @param \isys_cmdb_dao $dao
     */
    public function onObjectDeleted($objectID, $dao)
    {
        $database = $dao->get_database_component();

        if ($objectID > 0) {
            if ($database) {
                $database->query('DELETE FROM isys_search_idx WHERE isys_search_idx__reference = ' . (int)$objectID);
            } else {
                \isys_application::instance()->logger->warning('Search-Index error: Object with id ' . $objectID .
                    ' not removed from index. Database not available at this stage.', ['trace' => debug_backtrace()]);
            }
        }
    }

    /**
     * @param \isys_cmdb_dao_category $dao
     * @param                         $p_objectID
     */
    public function onBeforeRankRecord(\isys_cmdb_dao $dao, $objectID, $categoryID = null, $title, $row, $table, $currentStatus, $newStatus, $categoryType, $direction)
    {
        if ($objectID > 0 && $categoryID > 0 && $newStatus == C__RECORD_STATUS__PURGE) {
            $dao->get_database_component()
                ->query('DELETE FROM isys_search_idx WHERE ' . 'isys_search_idx__reference = ' . (int)$objectID . ' AND ' . 'isys_search_idx__key LIKE ' .
                    $dao->convert_sql_text('%.' . $dao->get_category() . '.' . $categoryID . '%') . ';');
        }
    }

    /**
     * Connect all signals
     */
    public function connect()
    {
        if (defined('WEB_CONTEXT') && constant('WEB_CONTEXT') === true) {
            $this->loadContainerDependencies();
        }

        if (!defined('WEB_CONTEXT') || constant('WEB_CONTEXT') !== true) {
            $this->createContainerDependencies();
        }

        $output = new BufferedOutput();
        $this->output = $output;

        $this->startTime = microtime(true);

        SignalCollection::get_instance()
            ->connect('mod.cmdb.afterCategoryEntrySave', [
                $this,
                'onAfterCategoryEntrySave'
            ])
            ->connect('mod.cmdb.objectDeleted', [
                $this,
                'onObjectDeleted'
            ])
            ->connect('mod.cmdb.beforeRankRecord', [
                $this,
                'onBeforeRankRecord'
            ])
            ->connect('mod.cmdb.massChangeApplied', [
                $this,
                'onMassChangeApplied'
            ])
            ->connect('mod.cmdb.multiEditSaved', [
                $this,
                'onMultiEditSaved'
            ])
            ->connect('mod.import_csv.afterImport', [
                $this,
                'onAfterCsvImport'
            ])
            ->connect('mod.cmdb.afterLegacyImport', [
                $this,
                'onPostImport'
            ])
        ->connect('mod.cmdb.beforeCategoryEntrySave', [
            $this,
            'onBeforeCategoryEntrySave'
        ]);
    }

    /**
     * @param OutputInterface $output
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;
    }

    /**
     * Load dependencies for search signals like index manager from container
     */
    private function loadContainerDependencies()
    {
        $this->manager = isys_application::instance()->container->get('idoit.search.index.manager');
    }

    /**
     * Container from modules not available, so create dependencies
     */
    private function createContainerDependencies()
    {
        $searchEngine = new Mysql(isys_application::instance()->container->get('database'));

        $collector = new CategoryCollector(isys_application::instance()->container->get('database'), [], []);

        $manager = new Manager($searchEngine, isys_application::instance()->container->get('event_dispatcher'));
        $manager->addCollector($collector, 'idoit.cmdb.search.index.category_collector');

        $this->manager = $manager;
    }
}
