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

/**
 * Class
 *
 * @class centreonGraphPoller
 * @description Class tp get metrics for a poller and return this on JSON
 */
class centreonGraphPoller
{
    /** @var */
    public $rrdOptions;

    /** @var */
    public $arguments;

    /** @var */
    public $metrics;

    /** @var */
    public $graphData;

    /** @var */
    protected $generalOpt;

    /** @var */
    protected $extraDatas;

    /** @var string Rrdtool command line */
    private $commandLine;

    /** @var int Poller id */
    private $pollerId;

    /** @var array Graph titles */
    private $title;

    /** @var array */
    private $options;

    /** @var array */
    private $differentStats;

    /** @var array */
    private $colors;

    /** @var CentreonDB */
    private $db;

    /** @var CentreonDB */
    private $dbMonitoring;

    /** @var string */
    private $graphName;

    /** @var string */
    private $nagiosStatsPath;

    /** @var array */
    private $metricsInfos = [];

    /**
     * centreonGraphPoller constructor
     *
     * @param CentreonDB $db
     * @param CentreonDB $dbMonitoring
     */
    public function __construct($db, $dbMonitoring)
    {
        $this->db = $db;
        $this->dbMonitoring = $dbMonitoring;

        $this->initGraphOptions();
        $this->initRrd();
    }

    /**
     * Set poller graph
     *
     * @param int $pollerId
     * @param string $graphName
     *
     * @return void
     */
    public function setPoller($pollerId, $graphName): void
    {
        $this->graphName = $graphName;
        $this->pollerId = $pollerId;
        $this->extraDatas = ['title' => $this->title[$graphName], 'base' => 1000];
        $this->rrdOptions = [];
        $this->arguments = [];

        $this->setRRDOption('imgformat', 'JSONTIME');
    }

    /**
     * @return string
     */
    public function getGraphName()
    {
        return $this->graphName;
    }

    /**
     * @param string $graphName
     */
    public function setGraphName($graphName = ''): void
    {
        $this->graphName = $graphName;
    }

    /**
     * Add argument rrdtool
     *
     * @param string $arg
     *
     * @return void
     */
    public function addArgument($arg): void
    {
        $this->arguments[] = $arg;
    }

    /**
     * Add argument rrdtool
     *
     * @param string $name the key
     * @param string $value
     *
     * @return void
     */
    public function setRRDOption($name, $value = null): void
    {
        if (str_contains($value, ' ')) {
            $value = "'" . $value . "'";
        }
        $this->rrdOptions[$name] = $value;
    }

    /**
     * Add arguments for rrdtool command line
     *
     * @param int $start
     * @param int $end
     *
     * @throws RuntimeException
     * @return void
     */
    public function buildCommandLine($start, $end): void
    {
        $this->extraDatas['start'] = $start;
        $this->extraDatas['end'] = $end;

        $this->setRRDOption('start', $start);
        $this->setRRDOption('end', $end);

        $this->metrics = [];

        $metrics = $this->differentStats[$this->options[$this->graphName]];

        $i = 0;
        foreach ($metrics as $metric) {
            $path = $this->nagiosStatsPath . '/perfmon-' . $this->pollerId . '/' . $this->options[$this->graphName];
            if (file_exists($path) === false) {
                throw new RuntimeException();
            }

            $displayformat = '%7.2lf';
            $this->addArgument('DEF:v' . $i . '=' . $path . ':' . $metric . ':AVERAGE');
            $this->addArgument('VDEF:v' . $i . $metric . '=v' . $i . ',AVERAGE');
            $this->addArgument('LINE1:v' . $i . '#0000ff:v' . $i);
            $this->addArgument('GPRINT:v' . $i . $metric . ':"' . $metric . "\:" . $displayformat . '" ');

            $this->metrics[] = ['metric_id' => $i, 'metric' => $metric, 'metric_legend' => $metric, 'legend' => $metric, 'ds_data' => ['ds_filled' => 0, 'ds_color_line' => $this->colors[$metric]]];

            $i++;
        }
    }

    /**
     * Get graph result
     *
     * @param int $start
     * @param int $end
     *
     * @throws RuntimeException
     * @return array
     */
    public function getGraph($start, $end)
    {
        $this->buildCommandLine($start, $end);

        return $this->getJsonStream();
    }

    /**
     * Init graph titles
     *
     * @return void
     */
    private function initGraphOptions(): void
    {
        $this->title = ['active_host_check' => _('Host Check Execution Time'), 'active_host_last' => _('Hosts Actively Checked'), 'host_latency' => _('Host check latency'), 'active_service_check' => _('Service Check Execution Time'), 'active_service_last' => _('Services Actively Checked'), 'service_latency' => _('Service check latency'), 'cmd_buffer' => _('Commands in buffer'), 'host_states' => _('Host status'), 'service_states' => _('Service status')];

        $this->colors = ['Min' => '#88b917', 'Max' => '#e00b3d', 'Average' => '#00bfb3', 'Last_Min' => '#00bfb3', 'Last_5_Min' => '#88b917', 'Last_15_Min' => '#ff9a13', 'Last_Hour' => '#F91D05', 'Up' => '#88b917', 'Down' => '#e00b3d', 'Unreach' => '#818285', 'Ok' => '#88b917', 'Warn' => '#ff9a13', 'Crit' => '#F91D05', 'Unk' => '#bcbdc0', 'In_Use' => '#88b917', 'Max_Used' => '#F91D05', 'Total_Available' => '#00bfb3'];

        $this->options = ['active_host_check' => 'nagios_active_host_execution.rrd', 'active_host_last' => 'nagios_active_host_last.rrd', 'host_latency' => 'nagios_active_host_latency.rrd', 'active_service_check' => 'nagios_active_service_execution.rrd', 'active_service_last' => 'nagios_active_service_last.rrd', 'service_latency' => 'nagios_active_service_latency.rrd', 'cmd_buffer' => 'nagios_cmd_buffer.rrd', 'host_states' => 'nagios_hosts_states.rrd', 'service_states' => 'nagios_services_states.rrd'];

        $this->differentStats = ['nagios_active_host_execution.rrd' => ['Min', 'Max', 'Average'], 'nagios_active_host_last.rrd' => ['Last_Min', 'Last_5_Min', 'Last_15_Min', 'Last_Hour'], 'nagios_active_host_latency.rrd' => ['Min', 'Max', 'Average'], 'nagios_active_service_execution.rrd' => ['Min', 'Max', 'Average'], 'nagios_active_service_last.rrd' => ['Last_Min', 'Last_5_Min', 'Last_15_Min', 'Last_Hour'], 'nagios_active_service_latency.rrd' => ['Min', 'Max', 'Average'], 'nagios_cmd_buffer.rrd' => ['In_Use', 'Max_Used', 'Total_Available'], 'nagios_hosts_states.rrd' => ['Up', 'Down', 'Unreach'], 'nagios_services_states.rrd' => ['Ok', 'Warn', 'Crit', 'Unk']];
    }

    /**
     * Get rrdtool options
     *
     * @throws PDOException
     * @return void
     */
    private function initRrd(): void
    {
        $DBRESULT = $this->db->query('SELECT * FROM `options`');

        $this->generalOpt = [];
        while ($option = $DBRESULT->fetch()) {
            $this->generalOpt[$option['key']] = $option['value'];
        }
        $DBRESULT->closeCursor();

        $DBRESULT2 = $this->dbMonitoring->query('SELECT RRDdatabase_nagios_stats_path FROM config');
        $nagiosStats = $DBRESULT2->fetch();
        $this->nagiosStatsPath = $nagiosStats['RRDdatabase_nagios_stats_path'];
        $DBRESULT2->closeCursor();
    }

    /**
     * Log message
     *
     * @param string $message
     *
     * @return void
     */
    private function log($message): void
    {
        if ($this->generalOpt['debug_rrdtool']
            && is_writable($this->generalOpt['debug_path'])) {
            error_log(
                '[' . date('d/m/Y H:i') . '] RDDTOOL : ' . $message . " \n",
                3,
                $this->generalOpt['debug_path'] . 'rrdtool.log'
            );
        }
    }

    /**
     * Get rrdtool result
     *
     * @return mixed
     */
    private function getJsonStream()
    {
        $commandLine = '';
        $commandLine = ' graph - ';

        foreach ($this->rrdOptions as $key => $value) {
            $commandLine .= '--' . $key;
            if (isset($value)) {
                if (preg_match('/\'/', $value)) {
                    $value = "'" . preg_replace('/\'/', ' ', $value) . "'";
                }
                $commandLine .= '=' . $value;
            }
            $commandLine .= ' ';
        }

        foreach ($this->arguments as $arg) {
            $commandLine .= ' ' . $arg . ' ';
        }
        $commandLine = preg_replace('/(\\$|`)/', '', $commandLine);
        $this->log($commandLine);

        if (is_writable($this->generalOpt['debug_path'])) {
            $stderr = ['file', $this->generalOpt['debug_path'] . '/rrdtool.log', 'a'];
        } else {
            $stderr = ['pipe', 'a'];
        }
        $descriptorspec = [
            0 => ['pipe', 'r'],
            // stdin is pipe for reading
            1 => ['pipe', 'w'],
            // stdout is pipe for writing
            2 => $stderr,
        ];

        $process = proc_open($this->generalOpt['rrdtool_path_bin'] . ' - ', $descriptorspec, $pipes, null, null);
        $this->graphData = ['global' => $this->extraDatas, 'metrics' => []];
        foreach ($this->metrics as $metric) {
            $this->graphData['metrics'][] = $metric;
        }

        if (is_resource($process)) {
            fwrite($pipes[0], $commandLine);
            fclose($pipes[0]);

            $str = stream_get_contents($pipes[1]);
            $returnValue = proc_close($process);

            $str = preg_replace('/OK u:.*$/', '', $str);
            $rrdData = json_decode($str, true);
        }

        $this->formatByMetrics($rrdData);

        return $this->graphData;
    }

    /**
     * Parse rrdtool result
     *
     * @param mixed $rrdData
     *
     * @return void
     */
    private function formatByMetrics($rrdData): void
    {
        $this->graphData['times'] = [];
        $size = count($rrdData['data']);
        $gprintsSize = count($rrdData['meta']['gprints']);

        for ($i = 0; $i < $size; $i++) {
            $this->graphData['times'][] = $rrdData['data'][$i][0];
        }

        $i = 1;
        $gprintsPos = 0;
        foreach ($this->graphData['metrics'] as &$metric) {
            $metric['data'] = [];
            $metric['prints'] = [];

            $insert = 0;
            $metricFullname = 'v' . $metric['metric_id'];
            for (; $gprintsPos < $gprintsSize; $gprintsPos++) {
                if (isset($rrdData['meta']['gprints'][$gprintsPos]['line'])) {
                    if ($rrdData['meta']['gprints'][$gprintsPos]['line'] == $metricFullname) {
                        $insert = 1;
                    } else {
                        break;
                    }
                } elseif ($insert == 1) {
                    $metric['prints'][] = array_values($rrdData['meta']['gprints'][$gprintsPos]);
                }
            }

            for ($j = 0; $j < $size; $j++) {
                $metric['data'][] = $rrdData['data'][$j][$i];
            }
            $i++;
        }
    }
}
