<?php

/*
 * Copyright 2005 - 2025 Centreon (https://www.centreon.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For more information : contact@centreon.com
 *
 */

ini_set('display_errors', 'Off');

require_once realpath(__DIR__ . '/../../../../../../bootstrap.php');
include_once _CENTREON_PATH_ . 'www/class/centreonUtils.class.php';
include_once _CENTREON_PATH_ . 'www/class/centreonXMLBGRequest.class.php';
include_once _CENTREON_PATH_ . 'www/include/monitoring/status/Common/common-Func.php';
include_once _CENTREON_PATH_ . 'www/include/common/common-Func.php';
include_once _CENTREON_PATH_ . 'www/class/centreonService.class.php';

// Create XML Request Objects
CentreonSession::start(1);
$obj = new CentreonXMLBGRequest($dependencyInjector, session_id(), 1, 1, 0, 1);
$svcObj = new CentreonService($obj->DB);

if (! isset($obj->session_id) || ! CentreonSession::checkSession($obj->session_id, $obj->DB)) {
    echo 'Bad Session ID';

    exit();
}

// Set Default Poller
$obj->getDefaultFilters();

/**
 * @var Centreon $centreon
 */
$centreon = $_SESSION['centreon'];

/**
 * true: URIs will correspond to deprecated pages
 * false: URIs will correspond to new page (Resource Status)
 */
$useDeprecatedPages = $centreon->user->doesShowDeprecatedPages();

// Check Arguments From GET tab
$o = isset($_GET['o']) ? HtmlAnalyzer::sanitizeAndRemoveTags($_GET['o']) : 'h';
$p = filter_input(INPUT_GET, 'p', FILTER_VALIDATE_INT, ['options' => ['default' => 2]]);
$num = filter_input(INPUT_GET, 'num', FILTER_VALIDATE_INT, ['options' => ['default' => 0]]);
$limit = filter_input(INPUT_GET, 'limit', FILTER_VALIDATE_INT, ['options' => ['default' => 20]]);
// if instance value is not set, displaying all active pollers linked resources
$instance = filter_var($obj->defaultPoller ?? -1, FILTER_VALIDATE_INT);
$hSearch = isset($_GET['host_search']) ? HtmlAnalyzer::sanitizeAndRemoveTags($_GET['host_search']) : '';
$sgSearch = isset($_GET['sg_search']) ? HtmlAnalyzer::sanitizeAndRemoveTags($_GET['sg_search']) : '';
$sort_type = isset($_GET['sort_type']) ? HtmlAnalyzer::sanitizeAndRemoveTags($_GET['sort_type']) : 'host_name';
$order = isset($_GET['order']) && $_GET['order'] === 'DESC' ? 'DESC' : 'ASC';

$kernel = App\Kernel::createForWeb();
$resourceController = $kernel->getContainer()->get(
    Centreon\Application\Controller\MonitoringResourceController::class
);

// saving bound values
$queryValues = [];
$queryValues2 = [];

$groupStr = implode(',', $obj->access->getAccessGroups()->getIds());

// Pre-fetch allowed service group IDs from config DB for non-admin users
$sgFilter = '';
if (! $obj->is_admin) {
    if ($groupStr === '') {
        $sgFilter = 'AND 1=0 ';
    } else {
        $allServiceGroupsAllowed = false;
        $query = <<<SQL
                SELECT
                    1
                FROM acl_resources ar
                INNER JOIN acl_res_group_relations argr
                    ON argr.acl_res_id = ar.acl_res_id
                WHERE
                    argr.acl_group_id IN ({$groupStr})
                    AND ar.all_servicegroups = '1'
                    AND ar.acl_res_activate = '1'
                LIMIT 1
            SQL;

        try {
            $allServiceGroupsAllowed = $obj->DB->fetchAssociative($query) !== false;
        } catch (Adaptation\Database\Connection\Exception\ConnectionException $e) {
            throw new Core\Common\Domain\Exception\RepositoryException(
                message: 'Error while checking if all service groups are allowed: ' . $e->getMessage(),
                context: [
                    'query' => $query,
                    'grouplistStr' => $grouplistStr,
                ],
                previous: $e
            );
        }

        if (! $allServiceGroupsAllowed) {
            $allowedSgIds = [];
            $query = <<<SQL
                    SELECT DISTINCT
                        arsr.sg_id
                    FROM acl_resources_sg_relations arsr
                    INNER JOIN acl_res_group_relations argr
                        ON argr.acl_res_id = arsr.acl_res_id
                    WHERE argr.acl_group_id IN ({$groupStr})
                SQL;

            try {
                foreach ($obj->DB->iterateAssociative($query) as $row) {
                    $allowedSgIds[] = (int) $row['sg_id'];
                }
            } catch (Adaptation\Database\Connection\Exception\ConnectionException $e) {
                throw new Core\Common\Domain\Exception\RepositoryException(
                    message: 'Error while fetching allowed service group IDs: ' . $e->getMessage(),
                    context: [
                        'query' => $query,
                        'grouplistStr' => $grouplistStr,
                    ],
                    previous: $e
                );
            }

            $sgFilter = $allowedSgIds === []
                ? 'AND 1=0 '
                : 'AND sg.servicegroup_id IN (' . implode(',', $allowedSgIds) . ') ';
        }
    }
}

// Backup poller selection
$obj->setInstanceHistory($instance);

/**
 * Prepare pagination
 */
$s_search = '';
// Display service problems
if (str_ends_with($o, '_pb')) {
    $s_search .= ' AND s.state != 0 AND s.state != 4 ';
}
// Display acknowledged services
if (str_ends_with($o, '_ack_1')) {
    $s_search .= " AND s.acknowledged = '1' ";
} elseif (str_ends_with($o, '_ack_0')) {
    // Display not acknowledged services
    $s_search .= ' AND s.state != 0 AND s.state != 4 AND s.acknowledged = 0 ';
}

$query = 'SELECT SQL_CALC_FOUND_ROWS DISTINCT 1 AS REALTIME, sg.servicegroup_id, h.host_id
    FROM servicegroups sg
    INNER JOIN services_servicegroups sgm ON sg.servicegroup_id = sgm.servicegroup_id
    INNER JOIN services s ON s.service_id = sgm.service_id
    INNER JOIN  hosts h ON sgm.host_id = h.host_id AND h.host_id = s.host_id ';

if (! $obj->is_admin && $groupStr !== '') {
    $query .= 'INNER JOIN centreon_acl
        ON centreon_acl.host_id = h.host_id
        AND centreon_acl.service_id = s.service_id
        AND centreon_acl.group_id IN (' . $groupStr . ') ';
}

$query .= 'WHERE 1 = 1 ';

// Servicegroup ACL
$query .= $sgFilter;

// Servicegroup search
if ($sgSearch != '') {
    $query .= 'AND sg.name = :sgSearch ';
    $queryValues['sgSearch'] = [
        PDO::PARAM_STR => $sgSearch,
    ];
}

// Host search
$h_search = '';
if ($hSearch != '') {
    $h_search .= ' AND h.name LIKE :hSearch ';
    // as this partial request is used in two queries, we need to bound it two times using two arrays
    // to avoid incoherent number of bound variables in the second query
    $queryValues['hSearch'] = $queryValues2['hSearch'] = [
        PDO::PARAM_STR => '%' . $hSearch . '%',
    ];
}
$query .= $h_search;

// Service search
$query .= $s_search;

// Poller search
if ($instance != -1) {
    $query .= ' AND h.instance_id = :instance ';
    $queryValues['instance'] = [
        PDO::PARAM_INT => $instance,
    ];
}

$query .= 'ORDER BY sg.name ' . $order . ' LIMIT :numLimit, :limit';
$queryValues['numLimit'] = [
    PDO::PARAM_INT => (int) ($num * $limit),
];
$queryValues['limit'] = [
    PDO::PARAM_INT => (int) $limit,
];

$dbResult = $obj->DBC->prepare($query);
foreach ($queryValues as $bindId => $bindData) {
    foreach ($bindData as $bindType => $bindValue) {
        $dbResult->bindValue($bindId, $bindValue, $bindType);
    }
}
$dbResult->execute();
$numRows = $obj->DBC->query('SELECT FOUND_ROWS() AS REALTIME')->fetchColumn();

/**
 * Create XML Flow
 */
$obj->XML = new CentreonXML();
$obj->XML->startElement('reponse');
$obj->XML->startElement('i');
$obj->XML->writeElement('numrows', $numRows);
$obj->XML->writeElement('num', $num);
$obj->XML->writeElement('limit', $limit);
$obj->XML->writeElement('host_name', _('Hosts'), 0);
$obj->XML->writeElement('services', _('Services'), 0);
$obj->XML->writeElement('p', $p);
$obj->XML->writeElement('sk', $obj->colorService[0]);
$obj->XML->writeElement('sw', $obj->colorService[1]);
$obj->XML->writeElement('sc', $obj->colorService[2]);
$obj->XML->writeElement('su', $obj->colorService[3]);
$obj->XML->writeElement('sp', $obj->colorService[4]);
$obj->XML->writeElement('s', '1');
$obj->XML->endElement();

$buildParameter = function (string $id, string $name) {
    return [
        'id' => $id,
        'name' => $name,
    ];
};

$buildServicesUri = function (string $hostname, array $statuses) use ($resourceController, $buildParameter) {
    return $resourceController->buildListingUri([
        'filter' => json_encode([
            'criterias' => [
                [
                    'name' => 'search',
                    'object_type' => null,
                    'type' => 'text',
                    'value' => 'h.name:^' . $hostname . '$',
                ],
                [
                    'name' => 'resource_types',
                    'object_type' => null,
                    'type' => 'multi_select',
                    'value' => [$buildParameter('service', 'Service')],
                ],
                [
                    'name' => 'statuses',
                    'object_type' => null,
                    'type' => 'multi_select',
                    'value' => $statuses,
                ],
            ],
        ]),
        'fromTopCounter' => 'true',
    ]);
};

$okStatus = $buildParameter('OK', 'Ok');
$warningStatus = $buildParameter('WARNING', 'Warning');
$criticalStatus = $buildParameter('CRITICAL', 'Critical');
$unknownStatus = $buildParameter('UNKNOWN', 'Unknown');
$pendingStatus = $buildParameter('PENDING', 'Pending');

// Construct query for servicegroups search
$sg_search = '';
if ($numRows > 0) {
    $sg_search .= 'AND (';
    $servicegroups = [];
    while ($row = $dbResult->fetch()) {
        $servicesgroups[$row['servicegroup_id']][] = $row['host_id'];
    }
    $servicegroupsSql1 = [];
    foreach ($servicesgroups as $key => $value) {
        $hostsSql = [];
        foreach ($value as $hostId) {
            $hostsSql[] = $hostId;
        }
        $servicegroupsSql1[] = '(sg.servicegroup_id = ' . $key . ' AND h.host_id IN ('
            . implode(',', $hostsSql) . ')) ';
    }
    $sg_search .= implode(' OR ', $servicegroupsSql1);
    $sg_search .= ') ';
    if ($sgSearch != '') {
        $sg_search .= 'AND sg.name = :sgSearch';
        $queryValues2['sgSearch'] = [
            PDO::PARAM_STR => $sgSearch,
        ];
    }

    $aclFromJoin = '';
    $aclWhereCondition = '';
    if (! $obj->is_admin) {
        if ($groupStr !== '') {
            $aclFromJoin = ', centreon_acl';
            $aclWhereCondition = 'AND centreon_acl.host_id = h.host_id
            AND centreon_acl.service_id = s.service_id
            AND centreon_acl.group_id IN (' . $groupStr . ') ';
        } else {
            $aclWhereCondition = 'AND 1=0 ';
        }
    }

    $query2 = 'SELECT SQL_CALC_FOUND_ROWS
        1 AS REALTIME,
        count(s.state) as count_state,
        sg.name AS sg_name,
        h.name AS host_name,
        h.state AS host_state,
        h.icon_image, h.host_id, s.state,
        (CASE s.state WHEN 0 THEN 3 WHEN 2 THEN 0 WHEN 3 THEN 2 ELSE s.state END) AS tri
        FROM servicegroups sg, services_servicegroups sgm, services s, hosts h' . $aclFromJoin . '
        WHERE h.host_id = s.host_id AND s.host_id = sgm.host_id AND s.service_id=sgm.service_id
        AND sg.servicegroup_id=sgm.servicegroup_id '
        . $s_search
        . $sg_search
        . $h_search
        . $sgFilter
        . $aclWhereCondition
        . ' GROUP BY sg_name,host_name,host_state,icon_image,host_id, s.state ORDER BY tri ASC ';

    $dbResult = $obj->DBC->prepare($query2);
    foreach ($queryValues2 as $bindId => $bindData) {
        foreach ($bindData as $bindType => $bindValue) {
            $dbResult->bindValue($bindId, $bindValue, $bindType);
        }
    }
    $dbResult->execute();

    $states = [0 => 'sk', 1 => 'sw', 2 => 'sc', 3 => 'su', 4 => 'sp'];

    $sg_list = [];
    while ($tab = $dbResult->fetch()) {
        $sg_list[$tab['sg_name']][$tab['host_name']]['host_id'] = $tab['host_id'];
        $sg_list[$tab['sg_name']][$tab['host_name']]['icon_image'] = $tab['icon_image'];
        $sg_list[$tab['sg_name']][$tab['host_name']]['host_state'] = $tab['host_state'];
        $sg_list[$tab['sg_name']][$tab['host_name']]['states'][$states[$tab['state']]] = $tab['count_state'];
    }

    $ct = 0;
    foreach ($sg_list as $sg => $h) {
        $count = 0;
        $ct++;
        $obj->XML->startElement('sg');
        $obj->XML->writeElement('sgn', CentreonUtils::escapeSecure($sg));
        $obj->XML->writeElement('o', $ct);

        foreach ($h as $host_name => $hostInfos) {
            $count++;
            $obj->XML->startElement('h');
            $obj->XML->writeAttribute('class', $obj->getNextLineClass());
            $obj->XML->writeElement('hn', CentreonUtils::escapeSecure($host_name), false);
            if ($hostInfos['icon_image']) {
                $obj->XML->writeElement('hico', $hostInfos['icon_image']);
            } else {
                $obj->XML->writeElement('hico', 'none');
            }
            $obj->XML->writeElement('hnl', CentreonUtils::escapeSecure(urlencode($host_name)));
            $obj->XML->writeElement('hcount', $count);
            $obj->XML->writeElement('hid', $hostInfos['host_id']);
            $obj->XML->writeElement('hs', _($obj->statusHost[$hostInfos['host_state']]));
            $obj->XML->writeElement('hc', $obj->colorHost[$hostInfos['host_state']]);
            $obj->XML->writeElement(
                'h_details_uri',
                $useDeprecatedPages
                    ? 'main.php?p=20202&o=hd&host_name=' . $host_name
                    : $resourceController->buildHostDetailsUri($hostInfos['host_id'])
            );
            $serviceListingDeprecatedUri = 'main.php?p=20201&o=svc&host_search=' . $host_name;
            $obj->XML->writeElement(
                's_listing_uri',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '$statusFilter='
                    : $resourceController->buildListingUri([
                        'filter' => json_encode([
                            'criterias' => [
                                [
                                    'name' => 'search',
                                    'object_type' => null,
                                    'type' => 'text',
                                    'value' => 'h.name:^' . $host_name . '$',
                                ],
                            ],
                        ]),
                        'fromTopCounter' => 'true',
                    ])
            );
            $obj->XML->writeElement(
                's_listing_ok',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '&statusFilter=ok'
                    : $buildServicesUri($host_name, [$okStatus])
            );
            $obj->XML->writeElement(
                's_listing_warning',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '&statusFilter=warning'
                    : $buildServicesUri($host_name, [$warningStatus])
            );
            $obj->XML->writeElement(
                's_listing_critical',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '&statusFilter=critical'
                    : $buildServicesUri($host_name, [$criticalStatus])
            );
            $obj->XML->writeElement(
                's_listing_unknown',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '&statusFilter=unknown'
                    : $buildServicesUri($host_name, [$unknownStatus])
            );
            $obj->XML->writeElement(
                's_listing_pending',
                $useDeprecatedPages
                    ? $serviceListingDeprecatedUri . '&statusFilter=pending'
                    : $buildServicesUri($host_name, [$pendingStatus])
            );

            foreach ($hostInfos['states'] as $state => $count) {
                $obj->XML->writeElement($state, $count);
            }
            $obj->XML->writeElement('chartIcon', returnSvg('www/img/icons/chart.svg', 'var(--icons-fill-color)', 18, 18));
            $obj->XML->writeElement('viewIcon', returnSvg('www/img/icons/view.svg', 'var(--icons-fill-color)', 18, 18));
            $obj->XML->endElement();
        }
        $obj->XML->endElement();
    }
}

$obj->XML->endElement();

// Send Header
$obj->header();

// Send XML
$obj->XML->output();
