<?php

use idoit\Component\CountingRotatingFileHandler;
use idoit\Component\FeatureManager\FeatureManager;
use idoit\Context\Context;
use idoit\Module\Cmdb\Search\Index\Signals as SearchIndexSignals;
use idoit\Module\SyneticsJdisc\Helper\JDiscWebProgressBar;
use idoit\Module\SyneticsJdisc\Helper\SSEMessenger;
use idoit\Module\SyneticsJdisc\Model\Dto\LogDto;
use idoit\Module\SyneticsJdisc\Model\ImportLogger;
use idoit\Module\SyneticsJdisc\Model\JDiscCommandQueue;
use idoit\Module\SyneticsJdisc\Model\LogsDao;
use Monolog\Handler\StreamHandler;
use Monolog\Level;

/**
 * AJAX
 *
 * @package     i-doit
 * @subpackage  General
 * @author      Leonard Fischer <lfischer@i-doit.org>
 * @version     1.0
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 * @since       0.9.9-9
 */
class isys_ajax_handler_jdisc extends isys_ajax_handler
{
    /**
     * This variable is attached to the stats message
     *
     * @var string
     */
    public static $m_additional_stats = '';

    /**
     * Timeout for the scan from the jdisc discovery category
     *
     * @var int
     */
    private $timeout = 60;

    /**
     * Retries for the scan from the jdisc discovery category
     *
     * @var int
     */
    private $retries = 1;

    const C__JDISC__TITLE_TRANSFORM__AS_IS = 1;
    const C__JDISC__TITLE_TRANSFORM__UPPERCASE = 2;
    const C__JDISC__TITLE_TRANSFORM__LOWERCASE = 3;
    /**
     * Init method, which gets called from the framework.
     *
     * @author  Leonard Fischer <lfischer@i-doit.org>
     */
    public function init()
    {
        if (!FeatureManager::isFeatureActive('jdisc-import')) {
            header('Content-Type: application/json');
            echo isys_format_json::encode([
                'success' => false,
                'message' => isys_application::instance()->container->get('language')
                    ->get('LC__FEATUREMANAGEMENT__FEATURE_IS_NOT_ACTIVE'),
            ]);
            $this->_die();
        }

        Context::instance()->setOrigin(Context::ORIGIN_GUI);
        switch ($_GET['func']) {
            case 'pause':
            case 'cancel':
            case 'continue':
                $this->enqueueImportCommand($_GET['func'], $_GET['queue'] ?? 'jdisc_import');
                break;

            case 'import':
                $this->process_import();
                break;

            case 'check_connection':
                $this->check_connection();
                break;

            case 'check_version':
                $this->check_version();
                break;
            case 'read_hostaddress_from_file':
                $this->read_hostaddress_from_file();
                break;
            case 'get_groups_and_profiles':
                $this->get_groups_and_profiles();
                break;
            case 'get_profile_data':
                $this->get_profile_data();
                break;
            case 'location_browser':
                $l_obj = new isys_popup_browser_location();
                $l_params = [
                    'p_strClass'        => 'jdisc_location',
                    'p_strPopupType'    => 'browser_location',
                    'containers_only'   => true,
                    'name'              => $_POST['field'],
                    'p_strSelectedID'   => $_POST['selected_object'],
                    'p_bInfoIconSpacer' => 0
                ];
                $templateInstance = isys_application::instance()->container->get('template');
                echo $l_obj->handle_smarty_include($templateInstance, $l_params);
                break;
            case 'discover_devices':
                $this->discover_devices();
                break;
            case 'discover':
                $this->discover($_GET['type']);
                break;
            case 'get_discovery_jobs':
                $this->get_discovery_jobs();
                break;
            case 'check_connection_discovery':
                $this->check_connection_discovery();
                break;
            case 'checkQueue':
                $this->manuallyTriggeredScansInQueue();
                break;
        }
        // End the request.
        $this->_die();
    }

    /**
     * Checks which JDisc version is currently running
     *
     * @author    Van Quyen Hoang <qhoang@i-doit.org>
     */
    protected function check_version()
    {
        header('Content-Type: application/json');
        $l_return = [];

        try {
            $l_module = isys_module_synetics_jdisc::factory();
            $l_jdisc_server_id = $_POST['jdisc_server'];
            $l_version_check = $l_module->check_version($l_jdisc_server_id);
            $l_current_version = $l_module->get_version($l_jdisc_server_id);
            $l_supported_version = $l_module::C__MODULE__JDISC__VERSION;

            if ($l_version_check === false) {
                $l_return['version_check'] = true;
                $l_return['message'] = '';
            } else {
                if ($l_current_version > $l_supported_version) {
                    $l_return['version_check'] = true;
                    $l_return['message'] = '';
                } else {
                    $l_return['version_check'] = false;
                    $l_return['message'] = isys_application::instance()->container->get('language')
                        ->get('LC__MODULE__JDISC__VERSION_CHECK_FAILED', [
                            $l_current_version,
                            $l_supported_version
                        ]);
                }
            }
        } catch (Exception $e) {
            $l_return['version_check'] = false;
            $l_return['message'] = $e->getMessage();
        }

        echo isys_format_json::encode($l_return);
    }

    /**
     * Method for checking the connection toward JDisc.
     *
     * @author  Leonard Fischer <lfischer@i-doit.org>
     */
    protected function check_connection()
    {
        header('Content-Type: application/json');
        $l_return = [];

        try {
            isys_module_synetics_jdisc::factory()
                ->get_connection($_POST['jdisc_server']);

            $l_return['connection'] = true;
            $l_return['message'] = isys_application::instance()->container->get('language')
                ->get('LC__MODULE__JDISC__CONNECTION_SUCCESS');
        } catch (Exception $e) {
            $l_return['connection'] = false;
            $l_return['message'] = $e->getMessage();
        }

        echo isys_format_json::encode($l_return, JSON_INVALID_UTF8_IGNORE);
    }

    /**
     * Method for processing the JDisc import.
     *
     * @todo    resolve JDiscImportCommand code duplication
     *
     * @author  Leonard Fischer <lfischer@i-doit.org>
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     */
    protected function process_import()
    {
        $database = isys_application::instance()->container->get('database');
        $response = [
            'type'    => 'log',
            'message' => null,
            'data'    => null,
        ];
        $showLoggingLink = false;

        $sse = SSEMessenger::getInstance();

        // Set memory limit
        if (($memLimit = isys_tenantsettings::get('system.memory-limit.jdisc', '2G'))) {
            ini_set('memory_limit', $memLimit);
        }

        $errorCallback = function (string $message) use ($sse) {
            $sse->sendLogMessage($message);
        };

        // Close session to enable another request while importing.
        isys_application::instance()->container
            ->get('session')->write_close();
        $language = isys_application::instance()->container->get('language');

        // set up logging
        if (isset($_GET['detailed-logging']) && $_GET['detailed-logging'] > 0) {
            switch ($_GET['detailed-logging']) {
                case '2':
                    $l_loglevel = Level::Debug->value;
                    break;
                default:
                case '1':
                    $l_loglevel = Level::Info->value;
                    break;
            }

            $showLoggingLink = true;
        } else {
            $l_loglevel = Level::Error->value;
        }

        // Groups are optional, profiles not.
        $l_group = ($_GET['group'] > 0) ? (int)$_GET['group'] : null;
        $l_profile = (int)$_GET['profile'];
        $l_clear_options['clear_identifiers'] = false;
        $l_clear_options['clear_single_identifier'] = false;
        $importMode = (int)$_GET['mode'];
        $l_jdisc = isys_module_synetics_jdisc::factory();

        // set up SSE headers
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');
        header('X-Accel-Buffering: no');

        if ($l_profile < 1 || !$l_jdisc->check_profile($l_profile)) {
            $sse->sendErrorMessage(
                $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__JDISC_PROFILE__DESCRIPTION')
            );
            return;
        }

        if (isset($_GET['regenerate-index'])) {
            $l_regenerate_search_index = (bool)$_GET['regenerate-index'];
        } else {
            $l_regenerate_search_index = true;
        }

        switch ($importMode) {
            case isys_module_synetics_jdisc::C__IMPORT_MODE__CREATE:
                $mode = isys_import_handler_cmdb::C__APPEND;
                break;
            case isys_module_synetics_jdisc::C__IMPORT_MODE__OVERWRITE_NEW_DISCOVERY:
                $l_clear_options['clear_identifiers'] = true;
                // no break
            case isys_module_synetics_jdisc::C__IMPORT_MODE__OVERWRITE:
                $l_jdisc->set_clear_mode(isys_import_handler_cmdb::C__OVERWRITE);
                $mode = isys_import_handler_cmdb::C__UPDATE;
                break;
            case isys_module_synetics_jdisc::C__IMPORT_MODE__UPDATE_NEW_DISCOVERY:
                $l_clear_options['clear_identifiers'] = true;
                $mode = isys_import_handler_cmdb::C__UPDATE;
                break;
            case isys_module_synetics_jdisc::C__IMPORT_MODE__CREATE_ONLY_NEW_DEVICES: // @see ID-7392 option append new only
                $l_clear_options['clear_identifiers'] = true;
                $mode = isys_import_handler_cmdb::C__APPEND;
                break;
            case isys_module_synetics_jdisc::C__IMPORT_MODE__UPDATE_EXISTING_ONLY:
                $l_clear_options['clear_identifiers'] = false;
                $mode = isys_import_handler_cmdb::C__UPDATE;
                break;
            case isys_module_synetics_jdisc::C__IMPORT_MODE__UPDATE:
            default:
                $mode = isys_import_handler_cmdb::C__UPDATE;
                break;
        }

        $queue    = $_GET['queue'] ?? 'jdisc_import';
        $serverId = (int) $_GET['jdisc_server'] ?? 0;
        $logDto   = LogDto::factory($serverId, $l_profile);
        $logsDao  = new LogsDao();
        $logsId   = null;
        $l_log    = ImportLogger::getLogger('Import JDisc', $logDto, $l_loglevel);
        $l_jdisc->setLogger($l_log);

        // Set database
        try {
            if (!$l_jdisc->switch_database($serverId)) {
                $response['success'] = false;
                $response['message'] = $language->get('LC__MODULE__JDISC__IMPORT__JDISC_SERVER__ERROR_MSG');
            } else {
                $l_is_jedi = $l_jdisc->is_jedi();
                /**
                 * Disconnect the onAfterCategoryEntrySave event to not always reindex the object in every category
                 * This is extremely important!
                 *
                 * An Index is done for all objects at the end of the request, if enabled via checkbox.
                 */
                SearchIndexSignals::instance()
                    ->disconnectOnAfterCategoryEntrySave();

                $l_log->info('Starting jdisc import..');
                $l_jdisc->set_mode($mode)
                    ->prepare_environment($l_profile, null, $l_log);

                if (!empty($_GET['filter_type'])) {
                    $l_jdisc->prepare_filter($_GET['filter_type'], $_GET['filter_data']);
                }

                // We only get the object id if we trigger the import via jdisc discovery category
                if (isset($_GET[C__CMDB__GET__OBJECT]) && $_GET[C__CMDB__GET__OBJECT] > 0) {
                    $profiles = $l_jdisc->m_dao->get_object_type_assignments_by_profile(
                        $l_profile,
                        ['matching_profile'],
                        ['object_type' => (int) $_GET['objTypeID']]
                    );

                    $matchingProfile = array_pop($profiles)['matching_profile'] ?: 1;
                    if (!isys_jdisc_dao_matching::instance()
                        ->match_jdisc_device([
                            'match_profile'      => $matchingProfile,
                            C__CMDB__GET__OBJECT => $_GET[C__CMDB__GET__OBJECT]
                        ])) {
                        // Match not found
                        $response = [
                            'type'    => 'error',
                            'message' => $language->get('LC__MODULE__JDISC__ERROR_DEVICE_NOT_FOUND_VIA_OBJECT_MATCHING'),
                        ];
                    }
                }

                $deviceAssignment = [];
                $deviceId         = null;
                $deviceIds        = null;
                $error            = null;
                if (!empty($_GET['deviceId'])) {
                    $deviceId = (int) $_GET['deviceId'];
                    $result   = $this->isValidDeviceId($deviceId, $language->get('LC__MODULE__JDISC__ERROR_DEVICE_NOT_FOUND_VIA_OBJECT_MATCHING'));
                    if ($result !== true) {
                        $error = $result;
                    }
                }
                if (!empty($_GET['deviceIds'])) {
                    $deviceIds = array_map(fn ($id) => (int)$id, explode(',', $_GET['deviceIds']));
                    foreach ($deviceIds as $id) {
                        $result = $this->isValidDeviceId($id, $language->get('LC__MODULE__JDISC__ERROR_DEVICE_NOT_FOUND_VIA_OBJECT_MATCHING'));
                        if ($result !== true) {
                            $error = $result;
                            break;
                        }
                    }
                }

                if ($error) {
                    $response = $error;
                } else {
                    if ($deviceId !== null || $deviceIds !== null) {
                        $deviceAssignment = [
                            'deviceType'  => (int) ($_GET['deviceType'] ?? 0),
                            'deviceTypes' => array_map(fn ($id) => (int) $id, explode(',', $_GET['deviceTypes'] ?? '')) ?? [],
                            'objTypeID'   => (int) ($_GET['objTypeID'] ?? 0),
                        ];
                        $l_jdisc->allowImportUnnamedDevices(true);

                        if ($deviceId !== null) {
                            $condition = " AND d.id = $deviceId ";
                        }
                        if ($deviceIds !== null) {
                            $deviceIdsString = implode(',', $deviceIds);
                            $condition = " AND d.id IN ($deviceIdsString) ";
                        }
                        isys_jdisc_dao_devices::instance($database)->set_device_filter_condition($condition);
                    }
                }

                // no errors in previous steps
                if (empty($response['message'])) {
                    // Getting the PDO.
                    $l_pdo = $l_jdisc->get_connection($serverId);

                    // Retrieve the result set for the objects to be imported.
                    $l_obj_res = $l_jdisc->retrieve_object_result($l_group, $l_profile, null, false, $deviceAssignment);

                    if ($l_obj_res) {
                        /* Identify import amount */
                        $totalCount = $l_pdo->num_rows($l_obj_res);

                        $sse->sendLogMessage(
                            '<br>' . $language->get('LC__MODULE__JDISC__IMPORT__JDISC_IMPORTING')
                        );

                        $progressBar = new JDiscWebProgressBar(1);
                        $progressBar->setMaxSteps($totalCount);

                        $commandQueue = new JDiscCommandQueue($database);
                        $commandQueue->setQueue($queue);

                        $l_raw_assignments = $l_jdisc->m_dao->get_object_type_assignments_by_profile($l_profile, null, null, false, false, $deviceAssignment);
                        $l_device_arr      = $l_jdisc->prepare_devices($l_obj_res, $progressBar, $l_clear_options, $l_raw_assignments, $commandQueue, $errorCallback);
                        $lastScannedDevice = $l_jdisc->getLastScannedItem();

                        // see @ID-7392 import only new jdisc objects
                        if ($importMode === isys_module_synetics_jdisc::C__IMPORT_MODE__CREATE_ONLY_NEW_DEVICES) {
                            $l_device_arr = array_filter($l_device_arr, function ($device) use ($lastScannedDevice) {
                                return $device['discoverytime'] > $lastScannedDevice && !$device['identifierObjID'];
                            });
                        }
                        // see @ID-7763 update only new jdisc objects
                        if ($importMode === isys_module_synetics_jdisc::C__IMPORT_MODE__UPDATE_EXISTING_ONLY) {
                            foreach ($l_device_arr as $l_device_key => $l_device) {
                                if (!$l_device['identifierObjID']) {
                                    unset($l_device_arr[$l_device_key]);
                                }
                            }
                        }
                        $l_import_count = count($l_device_arr);

                        if ($l_import_count > 0) {
                            $simpleDatabaseModel = isys_jdisc_dao_software::instance(isys_application::instance()->container->get('database'))->isSimpleDatabaseModel();

                            if ($l_import_count !== $totalCount) {
                                $l_log->info("Importing {$l_import_count} newly discovered objects! Processing Data ...");
                            } else {
                                $l_log->info("Importing all {$l_import_count} objects! Processing Data ...");
                            }

                            // END see @ID-7392 import only new jdisc objects

                            // @see ID-7391 transform imported objects titles
                            $l_device_arr = $l_jdisc->transformJdiscObjectsTitles($l_profile, $l_device_arr);
                            // END @see ID-7391 transform imported objects titles

                            $l_import_dao = new isys_module_dao_import_log($database);
                            isys_event_manager::getInstance()
                                ->set_import_id($l_import_dao->add_import_entry(
                                    $l_import_dao->get_import_type_by_const('C__IMPORT_TYPE__JDISC'),
                                    'JDisc Import (' . $l_import_count . ' device(s))'
                                ));
                            // Create an instance of the CMDB import
                            $l_import = new isys_import_handler_cmdb($l_log, $database, isys_cmdb_dao_jdisc::instance($database));
                            $l_import->set_empty_fields_mode(isys_import_handler_cmdb::C__KEEP);
                            $l_import->set_overwrite_ip_conflicts((bool)$_GET['overwrite_hostaddress']);
                            $l_import->set_general_header('JDisc');
                            $l_import->set_logbook_source(defined_or_default('C__LOGBOOK_SOURCE__JDISC'));

                            // Matching from JDisc device id to i-doit object id:
                            $l_jdisc_to_idoit = [];
                            // Cached object identifiers:
                            $l_object_ids = [];
                            $l_arr_device_ids = [];
                            /**
                             * Disable autocommits
                             */
                            isys_jdisc_dao_network::instance($database)
                                ->get_database_component()
                                ->set_autocommit(false);

                            $jdiscDaoImport = (new isys_jdisc_dao_import($database, $l_jdisc, $l_clear_options));
                            $currentStats = $jdiscDaoImport->getCurrentStats();

                            $sse->sendLogMessage(
                                $language->get('LC__MODULE__JDISC__IMPORT__JDISC_PROCESSING')
                            );

                            $progressBar = new JDiscWebProgressBar(2);
                            $progressBar->setMaxSteps($l_import_count);

                            $titleMap = $l_not_defined_types = [];
                            foreach ($l_device_arr as $l_obj_row) {
                                $commandQueue->processCommands();

                                if (!isset($l_obj_row['idoit_obj_type']) || $l_obj_row['idoit_obj_type'] === null) {
                                    if (!isset($l_not_defined_types[$l_obj_row['type_name']])) {
                                        $l_log->debug('JDisc type "' . $l_obj_row['type_name'] .
                                            '" is not properly defined in the profile. Skipping devices with JDisc type "' . $l_obj_row['type_name'] . '".');
                                    }
                                    $l_not_defined_types[$l_obj_row['type_name']]++;
                                    $progressBar->advance();
                                    continue;
                                }
                                if (in_array($l_obj_row['deviceid'], $l_arr_device_ids)) {
                                    $progressBar->advance();
                                    continue;
                                }
                                $l_arr_device_ids[] = $l_obj_row['deviceid'];

                                $l_prepared_data = $l_jdisc->prepare_object_data($l_obj_row, $l_jdisc_to_idoit, $l_object_ids);
                                if ($l_prepared_data === false) {
                                    // Skip this device
                                    $l_import_count--;
                                    $progressBar->advance();
                                    continue;
                                }

                                $progressBar->setMessage('Importing "' . $l_obj_row['name'] . '" (' . $l_obj_row['type_name'] . ') with ID #' . $l_jdisc_to_idoit[$l_obj_row['id']]);

                                $l_log->info('Importing "' . $l_obj_row['name'] . '" (' . $l_obj_row['type_name'] . ') with ID #' . $l_jdisc_to_idoit[$l_obj_row['id']]);
                                $l_log->debug('JDisc device ID: #' . $l_obj_row['id'] . ' | i-doit object ID #' . $l_jdisc_to_idoit[$l_obj_row['id']]);
                                // Prepare and import the data.
                                $l_import->reset()
                                    ->set_scantime()
                                    ->set_prepared_data($l_prepared_data['object'])
                                    ->set_connection_info($l_prepared_data['connections'])
                                    ->set_mode($mode)
                                    ->set_object_created_by_others(true)
                                    ->set_object_ids($l_object_ids)
                                    ->set_logbook_entries(isys_jdisc_dao_data::get_logbook_entries())
                                    ->import();
                                // Get the object id of the created object:
                                $l_last_object_id = $l_import::get_stored_objectID();
                                $l_jdisc_to_idoit[$l_obj_row['id']] = $l_last_object_id;
                                isys_jdisc_dao_devices::instance($database)
                                    ->set_jdisc_to_idoit_objects($l_obj_row['id'], $l_last_object_id);
                                $l_object_ids[$l_last_object_id] = $l_last_object_id;
                                $titleMap[$l_last_object_id] = $l_obj_row['name'];

                                /* Update CMDB Status */
                                if (isset($l_prepared_data['object'][$l_last_object_id]['cmdb_status']) && $l_prepared_data['object'][$l_last_object_id]['cmdb_status'] > 0) {
                                    isys_cmdb_dao::instance($database)
                                        ->set_object_cmdb_status($l_last_object_id, $l_prepared_data['object'][$l_last_object_id]['cmdb_status']);
                                }

                                unset($l_prepared_data);
                                $lastScannedDevice = $l_obj_row['discoverytime'] > $lastScannedDevice
                                    ? $l_obj_row['discoverytime'] : $lastScannedDevice;

                                // send result of device importing
                                if (!empty($deviceAssignment) && $l_last_object_id) {
                                    $this->sendDeviceImportResult($database, $sse, (int) $l_obj_row['id'], (int) $l_last_object_id);
                                }

                                $progressBar->advance();
                            }
                            $l_import->set_overwrite_ip_conflicts(false);
                            $l_jdisc->updateLastScannedDeviceTime($lastScannedDevice);
                            unset($l_import);
                            $l_log->debug('Starting the final step of the import: Referencing the data.');

                            $progressBar = new JDiscWebProgressBar(3);

                            $sse->sendLogMessage(
                                $language->get('LC__MODULE__JDISC__IMPORT__JDISC_PROCESSING_TO_IDOIT')
                            );

                            $jdiscDaoImport
                                ->setProgressBar($progressBar)
                                ->setCommandQueue($commandQueue)
                                ->executeFinalImportStep($l_jdisc_to_idoit, $titleMap);

                            $l_log->info('Finished!');

                            $l_affected_categories = $l_jdisc->get_cached_profile()['categoriesConstants'];
                            $indexCreationBenchmark = '';

                            // Only regenerate index and set summary count if there were any affected categories
                            if (is_array($l_affected_categories) && count($l_affected_categories)) {
                                $startTimeIndexCreation = microtime(true);

                                /* Adding additional categories*/
                                $l_categories = $l_affected_categories;
                                if (defined('C__CATG__NETWORK_LOG_PORT')) {
                                    $l_categories[] = 'C__CATG__NETWORK_LOG_PORT';
                                }
                                if (defined('C__CATG__JDISC_CA')) {
                                    $l_categories[] = 'C__CATG__JDISC_CA';
                                }
                                if (defined('C__CATS__ACCESS_POINT')) {
                                    $l_categories[] = 'C__CATS__ACCESS_POINT';
                                }

                                // Regenerate Search index
                                if ($l_regenerate_search_index) {
                                    $l_log->info('Regenerating search index..');
                                    SearchIndexSignals::instance()
                                        ->onPostImport($currentStats['time'], $l_categories, filter_defined_constants([
                                            'C__CATS__SERVICE',
                                            'C__CATS__APPLICATION',
                                            'C__CATS__DATABASE_SCHEMA',
                                            'C__CATS__DBMS',
                                            'C__CATS__DATABASE_INSTANCE',
                                            'C__CATS__ACCESS_POINT',
                                            'C__CATS__NET'
                                        ]));
                                    $indexCreationBenchmark = "Index creation took " . number_format(microtime(true) - $startTimeIndexCreation, 2) . " secs.\n";
                                }
                            }

                            /**
                             * Update statistics
                             */
                            $updatedStats = $jdiscDaoImport->getCurrentStats();

                            $l_sum_created_objects = $updatedStats['max_obj'] - $currentStats['max_obj'];
                            $l_vlan_stats = $updatedStats['max_vlan'] - $currentStats['max_vlan'];
                            $l_message = "Stats: \n\n" . ($l_sum_created_objects) . " objects created.   \n" . ($updatedStats['max_ports'] - $currentStats['max_ports']) .
                                " network ports created.   \n" . ($updatedStats['max_port_connections'] - $currentStats['max_port_connections']) .
                                " network ports connected to each other.   \n" . ($l_vlan_stats < 0 ? '0' : $l_vlan_stats) . " vlans assigned.   \n" .
                                ($l_vlan_stats < 0 ? ($l_vlan_stats * -1) . " vlans detached.   \n" : '') .
                                ($updatedStats['max_cluster_members'] - $currentStats['max_cluster_members']) . " cluster members attached.   \n" .
                                ($updatedStats['max_blades'] - $currentStats['max_blades']) . " blade servers attached.   \n" . ($updatedStats['max_sw'] - $currentStats['max_sw']) .
                                " software objects assigned. \n\n" . (self::$m_additional_stats ? self::$m_additional_stats . "  \n" : '') . $indexCreationBenchmark .
                                "Complete process took " . isys_glob_seconds_to_human_readable((int)($updatedStats['time'] - $currentStats['time'])) . ".  \n" .
                                "Memory peak usage: " . number_format(memory_get_peak_usage() / 1024 / 1024, 2, '.', '') . ' MB';

                            $l_log->info($l_message);

                            // @see ID-10562 create log file to calm down the user the all is ok
                            $logHandler = reset($l_log->getHandlers());
                            if (
                                $l_loglevel == Level::Error->value
                                && (
                                    (
                                        $logHandler instanceof CountingRotatingFileHandler
                                        && !$logHandler->getWriteCount()
                                    )
                                    || $logHandler instanceof StreamHandler
                                )
                            ) {
                                $logHandler->setLevel(Level::Info->value);
                                $l_log->info('No errors occured during the import.');
                            }

                            $logsDao->save(LogDto::STATUS_SUCCESS, $logDto);
                            $logsId = $logsDao->get_last_insert_id();

                            if ($showLoggingLink) {
                                $l_log_link = isys_application::instance()
                                    ->container
                                    ->get('route_generator')
                                    ->generate(
                                        'system.file-download',
                                        [
                                            'type'       => 'jdisc.log-file',
                                            'identifier' => $logsId,
                                        ]
                                    );
                                $l_message .= "\n\n" . '[' . $language->get('LC__MODULE__JDISC__IMPORT__SHOW_LOG') . '](' . $l_log_link . ')';
                            }
                            $l_not_imported_devices = (count($l_not_defined_types)) ? ' ' . array_sum($l_not_defined_types) .
                                ' device(s) could not be imported/updated because of their missing object type.' : '';
                            $response['data']['stats'] = "Done: $l_import_count device(s) imported/updated." . $l_not_imported_devices . "\n\n" .
                                $l_message; // attach stats to log output
                            $response['data']['log_icons'] = ImportLogger::getLevelIcons();
                            ob_end_clean();
                        } else {
                            $message = $importMode === isys_module_synetics_jdisc::C__IMPORT_MODE__CREATE_ONLY_NEW_DEVICES
                                ? $language->get('LC__MODULE__JDISC__NO_NEW_SCANNED_DEVICES_FOUND')
                                : $language->get('LC__UNIVERSAL__NO_OBJECTS_IMPORTED');
                            $l_log->warning($message);
                            $response['success'] = false;
                            $response['message'] = $message;
                        }
                    } else {
                        $response['success'] = false;
                        $response['message'] = $language->get('LC__MODULE__JDISC__ERROR_OBJECTTYPES_NOT_DEFINED_OR_ACTIVATED');
                        $l_log->error($response['message']);
                    }

                    if ($l_jdisc->importCloudSubscriptions) {
                        $database = isys_application::instance()->container->get('database');
                        $l_cloudSubscriptionsDao = isys_cmdb_dao_category_g_cloud_subscriptions::factory($database);
                        $response['success'] = $l_cloudSubscriptionsDao->importSubscriptions($l_jdisc);
                        $response['message'] = $language->get('LC__MODULE__JDISC__SUCCESS__CLOUD_IMPORT');
                    }
                }
            }
        } catch (Throwable $e) {
            $response['type'] = 'error';
            $response['success'] = false;
            $response['message'] = $e->getMessage() . '. File: ' . $e->getFile() . ' Line: ' . $e->getLine();
            $l_log->error($response['message']);

            // save log entry immediately when an exception occurs
            if (!$logsId) {
                $logsDao->save(LogDto::STATUS_FAILURE, $logDto);
            }
        }

        $status = (isset($response['success']) && $response['success'] === false) ? LogDto::STATUS_FAILURE : LogDto::STATUS_SUCCESS;
        if (!$logsId) {
            $logsDao->save($status, $logDto);
        }

        if (empty($response['type'])) {
            $response['type'] = 'log';
        }
        $response = json_encode($response);
        $sse->sendData($response);

        $sse->sendEndMessage();
    }

    /**
     * Reads text file which contains all host addresses which will be considered for the import
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    protected function read_hostaddress_from_file()
    {
        global $g_dirs;

        $l_uploader = new isys_library_fileupload;

        $l_filename = $l_uploader->getName();
        $l_file_prefix = 'jdisc_' . time() . '_';
        $l_upload_dir = realpath($g_dirs["fileman"]["target_dir"]) . '/';

        $l_result = $l_uploader->set_prefix($l_file_prefix)
            ->handleUpload($l_upload_dir);

        if ($l_result['success'] === true) {
            if (file_exists($l_upload_dir . $l_file_prefix . $l_filename)) {
                $l_data = [];
                $l_file_handler = fopen($l_upload_dir . $l_file_prefix . $l_filename, 'r');
                while ($l_ip = fgets($l_file_handler)) {
                    $l_ip = trim($l_ip);
                    if ($l_ip != '') {
                        $l_data[] = $l_ip;
                    }
                }
                fclose($l_file_handler);
                unlink($l_upload_dir . $l_file_prefix . $l_filename);

                $l_return = [
                    'success' => true,
                    'message' => null,
                    'data'    => $l_data
                ];
            } else {
                $l_return = [
                    'success' => false,
                    'message' => 'Failed to open file.',
                    'data'    => null
                ];
            }
        } else {
            $l_return = [
                'success' => false,
                'message' => 'Failed to open file.',
                'data'    => null
            ];
        }
        echo isys_format_json::encode($l_return);
    }

    /**
     * Retrieves all necessary info for the profile from the currently selected database
     */
    protected function get_profile_data()
    {
        header('Content-Type: application/json');
        $l_return = [
            'data'    => null,
            'success' => false
        ];

        try {
            /**
             * @var isys_module_synetics_jdisc
             */
            $l_module = new isys_module_synetics_jdisc();

            if ($l_module->switch_database($_POST['jdisc_server'])) {
                // JDisc operating systems.
                $l_jdisc_operating_systems = [];

                $l_entities = $l_module->get_jdisc_operating_systems();

                foreach ($l_entities as $l_entity) {
                    $l_value = $l_entity['osversion'];

                    if (!empty($l_entity['osfamily'])) {
                        $l_value .= ' (' . $l_entity['osfamily'] . ')';
                    }

                    $l_jdisc_operating_systems[$l_entity['id']] = $l_value;
                }

                $l_jdisc_operating_systems = array_unique($l_jdisc_operating_systems);
                $l_options_counters = $l_module->get_count_for_options();

                $l_return['success'] = true;
                $l_return['message'] = '';
                $l_return['data'] = [
                    'operating_systems' => $l_jdisc_operating_systems,
                    'options_counters'  => $l_options_counters
                ];
            } else {
                $l_return['message'] = isys_application::instance()->container->get('language')
                    ->get('LC__MODULE__JDISC__IMPORT__JDISC_SERVER__ERROR_MSG');
            }
        } catch (Exception $e) {
            $l_return['message'] = $e->getMessage();
        }

        echo isys_format_json::encode($l_return, JSON_INVALID_UTF8_IGNORE);
    }

    /**
     * Retrieves the groups and profiles for the selected jdisc server
     */
    protected function get_groups_and_profiles()
    {
        header('Content-Type: application/json');
        $l_return = [
            'data'    => null,
            'success' => false
        ];

        try {
            /**
             * @var isys_module_synetics_jdisc
             */
            $l_module = new isys_module_synetics_jdisc();

            if ($l_module->switch_database($_POST['jdisc_server'])) {
                $l_return = [
                    'success' => true,
                    'data'    => [
                        'default_profile' => null
                    ]
                ];
                if ($l_module->is_jedi()) {
                    $l_return['data']['groups'] = false;
                } else {
                    $l_groups = $l_module->get_jdisc_groups();
                    if (count($l_groups) > 0) {
                        foreach ($l_groups as $l_group) {
                            $l_return['data']['groups'][$l_group['id']] = $l_group['name'];
                        }
                        asort($l_return['data']['groups']);
                    } else {
                        $l_return['data']['groups'] = [];
                    }
                }

                $l_jdisc_server = $l_module->get_jdisc_servers();
                $servers = [];
                $defaultServer = null;
                $selectedServer = null;
                while ($jdiscServer = $l_jdisc_server->get_row()) {
                    $servers[] = $jdiscServer;
                    if ($jdiscServer['isys_jdisc_db__default_server']) {
                        $defaultServer = $jdiscServer;
                    }
                    if ($jdiscServer['isys_jdisc_db__id'] == $_POST['jdisc_server']) {
                        $selectedServer = $jdiscServer;
                    }
                }

                if ($defaultServer === null) {
                    $defaultServer = reset($servers);
                }

                $l_profiles = $l_module->get_jdisc_profiles();
                $profiles = [];

                foreach ($l_profiles as $profile) {
                    if ($profile['jdisc_server'] === null) {
                        $profile['jdisc_server'] = $defaultServer['isys_jdisc_db__id'];
                    }
                    if ($_POST['jdisc_server'] == $profile['jdisc_server']) {
                        $profiles[] = $profile;
                    }
                }

                if ($_POST['check_web_service']) {
                    $l_return['data']['graphql_active']     = $l_module->isGraphqlAvailable($_POST['jdisc_server']);
                    $l_return['data']['web_service_active'] = $l_return['data']['graphql_active'] ||
                        $l_module->web_service_active($_POST['jdisc_server']);
                }

                if (isset($_POST['object_type'])) {
                    global $g_comp_database;
                    $l_return['data']['default_profile'] = isys_cmdb_dao::instance($g_comp_database)
                        ->get_object_types($_POST['object_type'])
                        ->get_row_value('isys_obj_type__isys_jdisc_profile__id');
                }

                if (count($profiles) > 0) {
                    foreach ($profiles as $profile) {
                        $l_return['data']['profiles'][$profile['id']] = $profile['title'];
                    }

                    asort($l_return['data']['profiles']);
                } else {
                    $l_return['data']['profiles'] = [];
                }
            } else {
                $l_return['message'] = isys_application::instance()->container->get('language')
                    ->get('LC__MODULE__JDISC__IMPORT__JDISC_SERVER__ERROR_MSG');
            }
        } catch (Exception $e) {
            $l_return['message'] = $e->getMessage();
        }

        echo isys_format_json::encode($l_return);
    }

    /**
     * Trigger the discovery with the selected job
     */
    protected function discover_devices()
    {
        header('Content-Type: application/json');
        $l_module = isys_module_synetics_jdisc::factory();
        $l_jdisc_server = $l_module->get_jdisc_discovery_data($_POST['host'])
            ->get_row();
        $l_jdisc_job = isys_format_json::decode($_POST['job']);
        $l_host = $l_jdisc_server['isys_jdisc_db__host'];
        $l_username = $l_jdisc_server['isys_jdisc_db__discovery_username'];
        $l_password = isys_helper_crypt::decrypt($l_jdisc_server['isys_jdisc_db__discovery_password']);
        $l_port = $l_jdisc_server['isys_jdisc_db__discovery_port'];
        $l_protocol = $l_jdisc_server['isys_jdisc_db__discovery_protocol'];

        $l_return = [
            'message' => '',
            'success' => false,
            'data'    => []
        ];

        $l_discovery_obj = isys_jdisc_dao_discovery::get_instance($_POST['host']);
        try {
            $l_return['success'] = $l_discovery_obj->connect($l_host, $l_username, $l_password, $l_port, $l_protocol)
                ->set_discovery_job($l_jdisc_job)
                ->start_discovery_job();
            if ($l_return['success']) {
                $l_return['message'] = isys_application::instance()->container->get('language')
                    ->get('LC__MODULE__JDISC__DISCOVERY__JOBS__SUCCESS');
            } else {
                $l_return['message'] = isys_application::instance()->container->get('language')
                    ->get('LC__MODULE__JDISC__DISCOVERY__JOBS__FAILED');
            }
        } catch (Exception $e) {
            $l_return['message'] = isys_application::instance()->container->get('language')
                ->get('LC__MODULE__JDISC__DISCOVERY__JOBS__NO_CONNECTION');
        }
        echo isys_format_json::encode($l_return);
    }

    /**
     * Gets all JDisc discovery jobs from the Host
     */
    protected function get_discovery_jobs()
    {
        header('Content-Type: application/json');
        $l_module = isys_module_synetics_jdisc::factory();

        $l_jdisc_server = $l_module->get_jdisc_discovery_data($_POST['host'])
            ->get_row();
        $l_host = $l_jdisc_server['isys_jdisc_db__host'];

        $l_username = $l_jdisc_server['isys_jdisc_db__discovery_username'];
        $l_password = isys_helper_crypt::decrypt($l_jdisc_server['isys_jdisc_db__discovery_password']);
        $l_port = $l_jdisc_server['isys_jdisc_db__discovery_port'];
        $l_protocol = $l_jdisc_server['isys_jdisc_db__discovery_protocol'];
        $l_return = [
            'message' => '',
            'success' => false,
            'data'    => []
        ];

        $l_discovery_obj = isys_jdisc_dao_discovery::get_instance($_POST['host']);

        try {
            $l_discovery_jobs = $l_discovery_obj->connect($l_host, $l_username, $l_password, $l_port, $l_protocol)
                ->get_discovery_jobs();
            $l_discovery_obj->disconnect();
            if (is_array($l_discovery_jobs)) {
                if (count($l_discovery_jobs) > 0) {
                    $l_return['success'] = true;
                    $l_return['data'] = $l_discovery_jobs;
                } else {
                    $l_return['message'] = isys_application::instance()->container->get('language')
                        ->get('LC__MODULE__JDISC__DISCOVERY__JOBS__NO_DISCOVERY_JOBS');
                }
            }
        } catch (Exception $e) {
            $l_return['message'] = isys_application::instance()->container->get('language')
                ->get('LC__MODULE__JDISC__DISCOVERY__JOBS__NO_CONNECTION');
        }
        echo isys_format_json::encode($l_return);
    }

    /**
     * Method for checking the connection toward the JDisc Web Service.
     */
    protected function check_connection_discovery()
    {
        header('Content-Type: application/json');
        $l_return = [
            'message' => isys_application::instance()->container->get('language')
                ->get('LC__MODULE__JDISC__DISCOVERY__CONNECTION_FAILED'),
            'success' => false,
        ];
        $module = isys_module_synetics_jdisc::factory();
        if ($module->web_service_active($_POST['jdisc_server']) || $module->isGraphqlAvailable($_POST['jdisc_server'])) {
            $l_return['message'] = isys_application::instance()->container->get('language')
                ->get('LC__MODULE__JDISC__DISCOVERY__CONNECTION_SUCCESS');
            $l_return['success'] = true;
        }
        echo isys_format_json::encode($l_return);
    }

    /**
     * Discover single device
     *
     * @param $p_type
     *
     * @throws Exception
     */
    protected function discover($p_type)
    {
        $language = isys_application::instance()->container->get('language');
        $l_target = null;
        $l_return = [
            'message' => '',
            'success' => false,
            'data'    => []
        ];
        $l_ignored_addresses = [
            '0.0.0.0',
            '0',
            '127.0.0.1'
        ];

        $errorMsg = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__TARGET_TYPE__ERROR_NO_TYPE_SET');

        $l_discover_by_serialnumber = false;
        if (isset($_POST['targetType'])) {
            switch ($_POST['targetType']) {
                case isys_cmdb_dao_category_g_jdisc_discovery::C__JDISC_DISCOVERY__TARGET_TYPE__SERIALNUMBER:
                    $l_target                   = (!empty($_POST['serialnumber']) ? $_POST['serialnumber'] : null);
                    $errorMsg                   = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__TARGET_TYPE__ERROR_NO_SERIAL_NUMBER_SET');
                    $l_discover_by_serialnumber = true;
                    break;
                case isys_cmdb_dao_category_g_jdisc_discovery::C__JDISC_DISCOVERY__TARGET_TYPE__FQDN:
                    $l_target = ($_POST['hostname'] !== '-' ? $_POST['hostname'] : null);
                    $errorMsg = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__TARGET_TYPE__ERROR_NO_FQDN_SET');
                    break;
                case isys_cmdb_dao_category_g_jdisc_discovery::C__JDISC_DISCOVERY__TARGET_TYPE__IP:
                default:
                    $l_target = ($_POST['hostaddress'] !== '' ? $_POST['hostaddress'] : null);
                    $errorMsg = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__TARGET_TYPE__ERROR_NO_HOSTADDRESS_SET');
                    break;
            }
        }

        if ($l_target === null) {
            $l_return['message'] = $errorMsg;
        } else {
            $l_module = isys_module_synetics_jdisc::factory();
            $l_jdisc_server = $l_module->get_jdisc_discovery_data($_POST['host'])
                ->get_row();
            $l_module->switch_database($_POST['host']);
            $l_host = $l_jdisc_server['isys_jdisc_db__host'];
            $l_username = $l_jdisc_server['isys_jdisc_db__discovery_username'];
            $l_password = isys_helper_crypt::decrypt($l_jdisc_server['isys_jdisc_db__discovery_password']);
            $l_port = $l_jdisc_server['isys_jdisc_db__discovery_port'];
            $l_protocol = $l_jdisc_server['isys_jdisc_db__discovery_protocol'];

            $this->setTimeout((int)$l_jdisc_server['isys_jdisc_db__discovery_timeout']);
            $this->setRetries((int)$l_jdisc_server['isys_jdisc_db__discovery_import_retries']);
            if (method_exists($this, $p_type)) {
                try {
                    $l_discovery_obj = isys_jdisc_dao_discovery::get_instance($_POST['host'])
                        ->connect($l_host, $l_username, $l_password, $l_port, $l_protocol)
                        ->set_target($l_target);

                    if (is_a($l_discovery_obj, 'isys_jdisc_dao_discovery_soap') && $l_discover_by_serialnumber) {
                        throw new Exception("Can't be discovered by serial number without GraphQL support!");
                    }

                    $l_return = $this->$p_type(
                        $l_discovery_obj,
                        isys_jdisc_dao_data::instance(isys_application::instance()->container->get('database')),
                        $l_discover_by_serialnumber
                    );
                } catch (Exception $e) {
                    $l_return['message'] = $language->get('LC__MODULE__JDISC__DISCOVERY__JOBS__NO_CONNECTION');
                }
            }
        }
        echo isys_format_json::encode($l_return);
    }

    /**
     * Helper method which outputs the message
     *
     * @param string $p_message
     *
     * @return string
     * @author   Van Quyen Hoang <qhoang@i-doit.com>
     */
    private function output_message($p_message = '')
    {
        if ($p_message != '') {
            echo $p_message;
            ob_flush();
            flush();
            sleep(1);
        }

        return $p_message;
    }

    /**
     * Discover a specific device
     *
     * @param isys_jdisc_dao_discovery $p_discovery_obj
     * @param isys_jdisc_dao_data      $jdiscDaoData
     * @param boolean                  $p_discover_by_serialnumber
     *
     *
     * @return array
     * @throws Exception
     */
    private function discover_device(isys_jdisc_dao_discovery $p_discovery_obj, isys_jdisc_dao_data $jdiscDaoData, $p_discover_by_serialnumber = false)
    {
        $l_discovery_log = '';
        $success = false;
        $language = isys_application::instance()->container->get('language');
        $message = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__DISCOVERY_FINISHED');
        $data = 0;

        isys_application::instance()->container
            ->get('session')->write_close();

        $discover_method = 'discover_device';
        if ($p_discover_by_serialnumber) {
            $discover_method = 'discover_device_by_serialnumber';
        }

        if ($p_discovery_obj->$discover_method()) {
            $l_type = 'C__LOGBOOK_EVENT__CATEGORY_CHANGED';

            $l_discovery_log .= $this->output_message("Discovery of device \"" . $p_discovery_obj->get_target() . "\" started.\n");
            $queueCount = $jdiscDaoData->getDiscoveryQueueCount();

            if ($queueCount === 1) {
                $l_running_data = $p_discovery_obj->get_running_discover_status();
                $l_status = $l_running_data['status'];
                $l_last_log = $l_running_data['log'];
                $l_discovery_log .= $this->output_message($l_last_log . "\n");

                // Queue
                $sleepBeforeNextCall = 1;

                while ($l_status === 'Running') {
                    if (connection_aborted()) {
                        $this->output_message("Scan cancelled.\n");
                        $this->_die();
                    }
                    $l_running_data = $p_discovery_obj->get_running_discover_status();
                    $l_status = $l_running_data['status'];
                    if ($l_last_log !== $l_running_data['log'] && $l_running_data['log'] !== '' && $l_running_data['log'] !== null) {
                        $l_last_log = $l_running_data['log'];
                        $l_discovery_log .= $this->output_message($l_last_log . PHP_EOL);
                    }

                    // @See ID-5912 Do not remove this otherwise the connection can possibly break if the request is being called too fast
                    sleep($sleepBeforeNextCall);
                }
                $l_discovery_log .= $this->output_message("Finished scanning device.\n");
                $success = true;
            } else {
                $message = $language->get('LC__MODULE__JDISC__DISCOVIERY__ORDER_TO_SCAN_DEVICE_SEND');
                $l_discovery_log .= $this->output_message("There are {$queueCount} scan jobs inside the queue. Skipping life logging.\n");
                $data = [
                    'timeout' => $this->timeout,
                    'retries' => $this->retries
                ];
                $success = true;
            }
        } else {
            $message = $language->get('LC__CMDB__CATG__JDISC_DISCOVERY__SCAN_FAILED', [$p_discovery_obj->get_target()]);
            $l_discovery_log = $message . "\n";
            $l_type = 'C__LOGBOOK_EVENT__CATEGORY_CHANGED__NOT';
        }

        $this->write_discovery_log($l_type, $_POST[C__CMDB__GET__OBJECT], $_POST['objTypeID'], $l_discovery_log);

        return [
            'message' => $message,
            'success' => $success,
            'data' => $data
        ];
    }

    /**
     *
     */
    protected function manuallyTriggeredScansInQueue()
    {
        header('Content-Type: application/json');

        $return = [
            'data' => null,
            'message' => null,
            'success' => true
        ];

        try {
            $l_module = isys_module_synetics_jdisc::factory();
            $l_module->switch_database((int)$_POST['host']);

            $jdiscDaoData = isys_jdisc_dao_data::instance(isys_application::instance()->container->get('database'));

            if ($jdiscDaoData->checkManualScansInQueue() > 0) {
                $return['success'] = false;
            }
        } catch (Exception $e) {
            $return['message'] = $e->getMessage();
            $return['success'] = false;
        }

        echo isys_format_json::encode($return);
    }

    /**
     * @param integer $timeout
     */
    private function setTimeout($timeout)
    {
        $this->timeout = (int)$timeout;
    }

    /**
     * @param integer $retries
     */
    private function setRetries($retries)
    {
        $this->retries = (int)$retries;
    }

    /**
     * Write discovery log
     *
     * @param        $p_type
     * @param        $p_obj_id
     * @param        $p_obj_type_id
     * @param string $p_comment
     */
    private function write_discovery_log($p_type, $p_obj_id, $p_obj_type_id, $p_comment = '')
    {
        isys_event_manager::getInstance()
            ->triggerCMDBEvent(
                $p_type,
                null,
                $p_obj_id,
                $p_obj_type_id,
                'LC__CMDB__CATG__JDISC_DISCOVERY',
                null,
                $p_comment,
                null,
                null,
                null,
                0,
                defined_or_default('C__LOGBOOK_SOURCE__JDISC', null)
            );
    }

    /**
     * @param string $command
     * @param string $queue
     * @return void
     */
    protected function enqueueImportCommand(string $command, string $queue = 'jdisc_import'): void
    {
        $database = isys_application::instance()->container->get('database');
        $commandQueue = new JDiscCommandQueue($database);
        $commandQueue->setQueue($queue);
        $commandQueue->enqueue($command);
    }

    /**
     * Send device import result
     *
     * @param isys_component_database $database
     * @param SSEMessenger $sse
     * @param int $deviceId
     * @param int $objectId
     * @return void
     */
    private function sendDeviceImportResult(isys_component_database $database, SSEMessenger $sse, int $deviceId, int $objectId): void
    {
        $result = $database->query(
            <<<SQL
            SELECT
                isys_catg_jdisc_device_information_list__import_date AS import_date,
                isys_catg_jdisc_device_information_list__isys_obj__id AS objID,
                isys_obj__title AS title
            FROM isys_catg_jdisc_device_information_list
            LEFT JOIN isys_obj ON isys_obj__id = isys_catg_jdisc_device_information_list__isys_obj__id
            WHERE isys_catg_jdisc_device_information_list__isys_obj__id = ({$objectId})
            LIMIT 1
            SQL
        );
        $importDevice = $database->fetch_row_assoc($result);

        $sse->sendData(json_encode([
            'type'       => 'deviceImportResult',
            'deviceId'   => $deviceId,
            'objectId'   => $objectId,
            'importDate' => $importDevice['import_date'],
            'title'      => $importDevice["title"],
        ]));
    }

    /**
     * @param int $id
     * @param string $message
     * @return bool|array
     */
    private function isValidDeviceId(int $id, string $message): bool|array
    {
        if ($id < 1) {
            return [
                'type'    => 'error',
                'message' => $message,
            ];
        }
        return true;
    }
}
