<?php

use idoit\Module\JDisc\Graphql\Connector;
use idoit\Module\JDisc\Graphql\Query\IdentifyDevice;
use idoit\Module\JDisc\Graphql\Type\DeviceInputType;
use idoit\Component\Logger;

/**
 * I-doit -> JDisc Availability View
 *
 * @package     i-doit
 * @subpackage  Reports
 * @author      Paul Kolbovich <pkolbovich@i-doit.org>
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 */
class isys_report_view_jdisc_availability extends isys_report_view
{
    private int $devicesPerRequest = 50;

    /**
     * @return void
     */
    public function start(): void
    {
        $this->template->activate_editmode();

        $rules['servers'] = $this->getJDiscServers();
        $rules['types']   = $this->getTypes();
        $this->template->smarty_tom_add_rules('tom.content.bottom.content', $rules);

        $this->template->assign('servers', (bool) $rules['servers']['p_arData']);
        $this->template->assign('ajaxUrl', $this->getAJAXURL());
        $this->template->assign('devicesSize', $this->getDevicesSize());
        $this->template->assign('noDevices', $this->language->get('LC__REPORT__VIEW__JDISC_AVAILABILITY__NO_DEVICES'));
    }

    /**
     * @return array
     */
    private function getJDiscServers(): array
    {
        $servers       = [];
        $defaultServer = null;

        $res = (new isys_jdisc_dao($this->database))->get_jdisc_server_list();
        while ($row = $res->get_row()) {
            $servers[$row['isys_jdisc_db__id']] = "{$row['isys_jdisc_db__host']}:{$row['isys_jdisc_db__database']} ({$row['isys_jdisc_db__title']})";
            if ($row['isys_jdisc_db__default_server']) {
                $defaultServer = $row['isys_jdisc_db__id'];
            }
        }

        if (!$defaultServer) {
            $defaultServer = key($servers);
        }

        return [
            'p_arData'              => $servers,
            'p_strSelectedID'       => $defaultServer,
            'inputGroupMarginClass' => ' ',
            'p_bDbFieldNN'          => true,
        ];
    }

    /**
     * @return array
     */
    private function getTypes(): array
    {
        $types = [];

        $res = isys_cmdb_dao::instance($this->database)
            ->get_objtype(null, false, C__RECORD_STATUS__NORMAL);
        while ($row = $res->get_row()) {
            $types[$row['isys_obj_type__id']] = $this->language->get($row['isys_obj_type__title']);
        }
        asort($types);

        $types = [0 => $this->language->get('LC__UNIVERSAL__ALL')] + $types;

        return [
            'p_arData'              => $types,
            'p_strSelectedID'       => 0,
            'inputGroupMarginClass' => ' ',
            'p_bDbFieldNN'          => true,
            'p_bSort'               => false,
        ];
    }

    /**
     * @return string
     */
    private function getAJAXURL(): string
    {
        return isys_helper_link::create_url([
            C__GET__AJAX             => 1,
            C__GET__MODULE_ID        => C__MODULE__REPORT,
            C__GET__REPORT_PAGE      => C__REPORT_PAGE__VIEWS,
            C__GET__REPORT_REPORT_ID => 'isys_report_view_jdisc_availability'
        ]);
    }

    /**
     * @param  int $type
     *
     * @return int
     */
    private function getDevicesSize(int $type = 0): int
    {
        $status       = (int) C__RECORD_STATUS__NORMAL;
        $typeSelector = '';
        if ($type) {
            $typeSelector = "AND isys_obj__isys_obj_type__id = {$type}";
        }

        return (int) $this->database->fetch_row_assoc(
            $this->database->query(
                <<<SQL
                SELECT
                    COUNT(isys_obj__id) AS cnt
                    FROM isys_obj
                    JOIN isys_catg_jdisc_device_information_list
                        ON isys_catg_jdisc_device_information_list__isys_obj__id = isys_obj__id
                    WHERE
                        isys_obj__status = {$status}
                        {$typeSelector}
                SQL
            )
        )['cnt'];
    }

    /**
     * @return void
     */
    public function ajax_request(): void
    {
        $result = [];

        $serverId          = (int) $_POST['server'];
        $type              = (int) $_POST['type'];
        $devicesProcessed  = (int) $_POST['devicesProcessed'];
        $devicesSize       = $this->getDevicesSize($type);
        $onlyUnavailable   = (int) $_POST['onlyUnavailable'];
        $devicesPerRequest = $this->devicesPerRequest;
        $status            = (int) C__RECORD_STATUS__NORMAL;
        $jdiscTypeId       = (int) C__CATG__IDENTIFIER_TYPE__JDISC;

        $this->initJDiscMatching($serverId);
        $connector = $this->getConnector($serverId);

        // get devices
        $devices = [];
        if ($connector) {
            $typeSelector = '';
            if ($type) {
                $typeSelector = "AND isys_obj__isys_obj_type__id = {$type}";
            }
            do {
                $res = $this->database->query(
                    <<<SQL
                    SELECT
                        isys_obj__id AS id,
                        isys_obj__title AS title,
                        isys_jdisc_mapping__jdisc_serial_number AS mapping_serial_number,
                        isys_jdisc_mapping__jdisc_mac AS mapping_mac,
                        isys_catg_model_list__serial AS model_serial_number,
                        GROUP_CONCAT(DISTINCT isys_catg_log_port_list__mac) AS port_list_mac,
                        isys_catg_identifier_list__value AS jdisc_id
                    FROM isys_obj
                    JOIN isys_catg_jdisc_device_information_list
                        ON isys_catg_jdisc_device_information_list__isys_obj__id = isys_obj__id
                    LEFT JOIN isys_jdisc_mapping
                        ON isys_jdisc_mapping__reference_id = isys_obj__id
                    LEFT JOIN isys_catg_model_list
                        ON isys_catg_model_list__isys_obj__id = isys_obj__id
                    LEFT JOIN isys_catg_log_port_list
                        ON isys_catg_log_port_list__isys_obj__id = isys_obj__id
                    LEFT JOIN isys_catg_identifier_list
                        ON
                            isys_catg_identifier_list__isys_obj__id = isys_obj__id
                            AND isys_catg_identifier_list__isys_catg_identifier_type__id = {$jdiscTypeId}
                    WHERE
                        isys_obj__status = {$status}
                        {$typeSelector}
                    GROUP BY isys_obj__id
                    ORDER BY id
                    LIMIT {$devicesProcessed}, {$devicesPerRequest}
                    SQL
                );
                $devicesNumRows = $this->database->num_rows($res);
                while ($row = $this->database->fetch_row_assoc($res)) {
                    $devicesProcessed++;
                    $device = [
                        'id'           => $row['id'],
                        'jdiscId'      => $row['jdisc_id'],
                        'title'        => $row['title'],
                        'mac'          => $row['mapping_mac'] ?? $row['port_list_mac'],
                        'serialnumber' => $row['mapping_serial_number'] ?? $row['model_serial_number'],
                        'jdisc'        => false,
                    ];

                    // check JDisc device availability
                    $jdiscFound = $this->checkJDisc($connector, $device);

                    if ($onlyUnavailable && $jdiscFound) {
                        continue;
                    }

                    $devices[] = [
                        'id'    => $device['id'],
                        'title' => $device['title'],
                        'jdisc' => [
                            'class' => $jdiscFound ? 'text-green' : 'text-red',
                            'value' => $jdiscFound ? $this->language->get('LC__UNIVERSAL__YES') : $this->language->get('LC__UNIVERSAL__NO'),
                        ],
                    ];

                    if (count($devices) == $devicesPerRequest) {
                        break;
                    }
                }
            } while (count($devices) < $devicesPerRequest && $devicesProcessed < $devicesSize);

            $result = [
                'success'          => true,
                'devices'          => $devices,
                'caller'           => (string) $_POST['caller'],
                'devicesProcessed' => $devicesProcessed,
                'devicesSize'      => $devicesSize,
            ];
        } else {
            $result = [
                'success' => false,
            ];
        }

        header('Content-Type: application/json');
        echo isys_format_json::encode($result);
        exit;
    }

    /**
     * @param  int $serverId
     *
     * @return void
     */
    private function initJDiscMatching(int $serverId): void
    {
        // init jdisc_pdo
        $jdisc = isys_module_jdisc::factory();
        $jdisc->switch_database($serverId);

        // init isys_jdisc_dao_matching
        isys_jdisc_dao_matching::initialize(
            1,
            $serverId,
            new isys_jdisc_dao_data(
                $this->database,
                isys_application::instance()->container->get('jdisc_pdo')
            )
        );
    }

    /**
     * @param  int $serverId
     *
     * @return Connector|null
     */
    private function getConnector(int $serverId): ?Connector
    {
        $connector = null;
        $jdisc     = isys_module_jdisc::factory();

        if ($jdisc->isGraphqlAvailable($serverId)) {
            $connector = Connector::instance($serverId);
            $connector->connect();
        } else {
            isys_notify::error(
                $this->language->get('LC__REPORT__VIEW__JDISC_AVAILABILITY__NO_GRAPHQL')
            );
        }

        return $connector;
    }

    /**
     * @param  Connector $connector
     * @param  array     $device
     *
     * @return bool
     */
    private function checkJDisc(Connector $connector, array $device): bool
    {
        if ($device['serialnumber'] || $device['mac']) {
            $networkInterfaces = [];
            if ($device['mac']) {
                $macs = explode(',', $device['mac']);
                foreach ($macs as $mac) {
                    $networkInterfaces[] = ['physicalAddress' => $mac];
                }
            }

            $query = new IdentifyDevice();
            $query->setSelections([
                'id',
            ]);
            $query->setParameters([
                new DeviceInputType(
                    'device',
                    [
                        'serialNumber'      => $device['serialnumber'],
                        'networkInterfaces' => $networkInterfaces,
                    ]
                ),
            ]);
            $data = $connector->query($query);

            return !empty($data['device']);
        }

        if ($device['jdiscId']) {
            $objectId = isys_jdisc_dao_matching::instance()
                ->get_object_id_by_device_id($device['jdiscId']);
            return $objectId == $device['id'];
        }

        return false;
    }

    /**
     * @return string
     */
    public static function name(): string
    {
        return 'LC__REPORT__VIEW__JDISC_AVAILABILITY__TITLE';
    }

    /**
     * @return string
     */
    public static function description(): string
    {
        return 'LC__REPORT__VIEW__JDISC_AVAILABILITY__DESCRIPTION';
    }

    /**
     * @return string
     */
    public function template(): string
    {
        return isys_module_report::getPath() . 'templates/view_jdisc_availability.tpl';
    }

    /**
     * @return string
     */
    public static function viewtype() :string
    {
        return 'LC__CMDB__OBJTYPE__CATG';
    }
}
