<?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
 *
 */

require_once _CENTREON_PATH_ . '/www/class/centreonDB.class.php';
require_once _CENTREON_PATH_ . '/www/class/centreonGraphService.class.php';
require_once __DIR__ . '/centreon_configuration_objects.class.php';

/**
 * Class
 *
 * @class CentreonMonitoringMetric
 */
class CentreonMonitoringMetric extends CentreonConfigurationObjects
{
    /** @var CentreonDB */
    protected $pearDBMonitoring;

    /**
     * CentreonMonitoringMetric constructor
     */
    public function __construct()
    {
        $this->pearDBMonitoring = new CentreonDB('centstorage');
        parent::__construct();
    }

    /**
     * @throws Exception
     * @return array{items: array{id: string, text: string}, total: int}
     */
    public function getList(): array
    {
        global $centreon;

        $configurationDbName = db;

        $aclSubQuery = '';
        $whereClause = '';
        $limitClause = '';

        $queryValues = [];

        if (! $centreon->user->admin) {
            $aclSubQuery = <<<SQL
                INNER JOIN `index_data` i
                    ON i.`id` = m.`index_id`
                INNER JOIN `centreon_acl` acl
                    ON acl.`host_id` = i.`host_id`
                    AND acl.`service_id` = i.`service_id`
                INNER JOIN {$configurationDbName}.`acl_groups` ag
                    ON ag.`acl_group_id` = acl.`group_id`
                    AND ag.acl_group_activate = '1'
                    AND (`acl_group_id` IN (SELECT `acl_group_id`
                            FROM {$configurationDbName}.`acl_group_contacts_relations`
                            WHERE `contact_contact_id` = :contact_id)
                        OR `acl_group_id` IN (SELECT `acl_group_id`
                            FROM {$configurationDbName}.`acl_group_contactgroups_relations` acgr
                            INNER JOIN {$configurationDbName}.`contactgroup_contact_relation` cgcr
                                ON cgcr.`contactgroup_cg_id` = acgr.cg_cg_id
                            WHERE cgcr.`contact_contact_id` = :contact_id
                        )
                    )
                SQL;

            $queryValues[':contact_id'] = [$centreon->user->user_id, PDO::PARAM_INT];
        }

        if (isset($this->arguments['q'])) {
            $whereClause = <<<'SQL'
                WHERE `metric_name` LIKE :name
                SQL;

            $queryValues[':name'] = ['%' . (string) $this->arguments['q'] . '%', PDO::PARAM_STR];
        }

        if (isset($this->arguments['page_limit'], $this->arguments['page'])) {
            if (
                filter_var(($limit = $this->arguments['page_limit']), FILTER_VALIDATE_INT) === false
                || filter_var(($page = $this->arguments['page']), FILTER_VALIDATE_INT) === false
            ) {
                throw new InvalidArgumentException('Pagination parameters must be integers');
            }

            if ($page < 1) {
                throw new InvalidArgumentException('Page number must be greater than zero');
            }

            $offset = ($page - 1) * $limit;
            $limitClause = 'LIMIT :offset, :limit';

            $queryValues[':offset'] = [$offset, PDO::PARAM_INT];
            $queryValues[':limit'] = [$limit, PDO::PARAM_INT];
        }

        $query = <<<SQL
            SELECT SQL_CALC_FOUND_ROWS DISTINCT `metric_name`
            FROM `metrics` m
            {$aclSubQuery}
            {$whereClause}
            ORDER BY `metric_name`
            {$limitClause}
            SQL;

        $stmt = $this->pearDBMonitoring->prepare($query);
        foreach ($queryValues as $name => $parameters) {
            [$value, $type] = $parameters;
            $stmt->bindValue($name, $value, $type);
        }
        $stmt->execute();

        $metrics = [];
        while ($row = $stmt->fetch()) {
            $metrics[] = [
                'id' => (string) $row['metric_name'],
                'text' => (string) $row['metric_name'],
            ];
        }

        return [
            'items' => $metrics,
            'total' => (int) $this->pearDBMonitoring->numberRows(),
        ];
    }

    /**
     * @throws Exception
     * @throws RestBadRequestException
     * @throws RestForbiddenException
     * @throws RestNotFoundException
     * @return array
     */
    public function getMetricsDataByService()
    {
        global $centreon;

        $userId = $centreon->user->user_id;
        $isAdmin = $centreon->user->admin;

        // Get ACL if user is not admin
        if (! $isAdmin) {
            $acl = new CentreonACL($userId, $isAdmin);
            $aclGroups = $acl->getAccessGroupsString();
        }

        // Validate options
        if (isset($this->arguments['start']) === false
            || is_numeric($this->arguments['start']) === false
            || isset($this->arguments['end']) === false
            || is_numeric($this->arguments['end']) === false
        ) {
            throw new RestBadRequestException('Bad parameters');
        }

        $start = $this->arguments['start'];
        $end = $this->arguments['end'];

        // Get the numbers of points
        $rows = 200;
        if (isset($this->arguments['rows'])) {
            if (is_numeric($this->arguments['rows']) === false) {
                throw new RestBadRequestException('Bad parameters');
            }
            $rows = $this->arguments['rows'];
        }
        if ($rows < 10) {
            throw new RestBadRequestException('The rows must be greater as 10');
        }

        if (isset($this->arguments['ids']) === false) {
            self::sendResult([]);
        }

        // Get the list of service ID
        $ids = explode(',', $this->arguments['ids']);
        $result = [];

        foreach ($ids as $id) {
            [$hostId, $serviceId] = explode('_', $id);
            if (is_numeric($hostId) === false || is_numeric($serviceId) === false) {
                throw new RestBadRequestException('Bad parameters');
            }

            // Check ACL is not admin
            if (! $isAdmin) {
                $query = 'SELECT service_id '
                    . 'FROM centreon_acl '
                    . 'WHERE host_id = :hostId '
                    . 'AND service_id = :serviceId '
                    . 'AND group_id IN (' . $aclGroups . ')';
                $stmt = $this->pearDBMonitoring->prepare($query);
                $stmt->bindParam(':hostId', $hostId, PDO::PARAM_INT);
                $stmt->bindParam(':serviceId', $serviceId, PDO::PARAM_INT);
                $dbResult = $stmt->execute();
                if (! $dbResult) {
                    throw new Exception('An error occured');
                }
                if ($stmt->rowCount() == 0) {
                    throw new RestForbiddenException('Access denied');
                }
            }

            // Prepare graph
            try {
                // Get index data
                $indexData = CentreonGraphService::getIndexId($hostId, $serviceId, $this->pearDBMonitoring);
                $graph = new CentreonGraphService($indexData, $userId);
            } catch (Exception $e) {
                throw new RestNotFoundException('Graph not found');
            }
            $graph->setRRDOption('start', $start);
            $graph->setRRDOption('end', $end);
            $graph->initCurveList();
            $graph->createLegend();

            $serviceData = $graph->getData($rows);
            // Replace NaN
            $counter = count($serviceData);
            for ($i = 0; $i < $counter; $i++) {
                if (isset($serviceData[$i]['data'])) {
                    $times = array_keys($serviceData[$i]['data']);
                    $values = array_map(
                        [$this, 'convertNaN'],
                        array_values($serviceData[$i]['data'])
                    );
                }
                $serviceData[$i]['data'] = $values;
                $serviceData[$i]['label'] = $serviceData[$i]['legend'];
                unset($serviceData[$i]['legend']);
                $serviceData[$i]['type'] = $serviceData[$i]['graph_type'];
                unset($serviceData[$i]['graph_type']);
            }
            $result[] = ['service_id' => $id, 'data' => $serviceData, 'times' => $times, 'size' => $rows];
        }

        return $result;
    }

    /**
     * Function for test is a value is NaN
     *
     * @param mixed $element The element to test
     * @return mixed|null null if NaN else the element
     */
    protected function convertNaN($element)
    {
        if (strtoupper($element) == 'NAN') {
            return null;
        }

        return $element;
    }
}
