<?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 __DIR__ . '/centreon_configuration_objects.class.php';
require_once __DIR__ . '/centreon_realtime_base.class.php';

/**
 * Class
 *
 * @class CentreonRealtimeServices
 */
class CentreonRealtimeServices extends CentreonRealtimeBase
{
    /** @var int|string|null */
    public $servicegroup;

    /** @var string|null */
    public $searchOutput;

    /** @var CentreonACL */
    protected $aclObj;

    /** @var int */
    protected $admin;

    // parameters
    /** @var int|null */
    protected $limit;

    /** @var int|null */
    protected $number;

    /** @var string|null */
    protected $status;

    /** @var string|null */
    protected $hostgroup;

    /** @var string|null */
    protected $search;

    /** @var string|null */
    protected $searchHost;

    /** @var string|null */
    protected $viewType;

    /** @var string|null */
    protected $sortType;

    /** @var string|null */
    protected $order;

    /** @var int|null */
    protected $instance;

    /** @var string|null */
    protected $criticality;

    /** @var string|null */
    protected $fieldList;

    /** @var array|null */
    protected $criticalityList;

    /**
     * CentreonConfigurationService constructor
     */
    public function __construct()
    {
        global $centreon;

        parent::__construct();

        // Init ACL
        if (! $centreon->user->admin) {
            $this->admin = 0;
            $this->aclObj = new CentreonACL($centreon->user->user_id, $centreon->user->admin);
        } else {
            $this->admin = 1;
        }

        // Init Values
        $this->getCriticality();
    }

    /**
     * @throws PDOException
     * @throws RestBadRequestException
     * @return array
     */
    public function getList()
    {
        $this->setServiceFilters();
        $this->setServiceFieldList();

        return $this->getServiceState();
    }

    /**
     * @throws PDOException
     * @throws RestBadRequestException
     * @return array
     */
    public function getServiceState()
    {
        $queryValues = [];

        /** * *************************************************
         * Get Service status
         */
        $query = 'SELECT SQL_CALC_FOUND_ROWS DISTINCT ' . $this->fieldList . ' ';
        $query .= ' FROM hosts h, instances i ';
        if (isset($this->hostgroup) && $this->hostgroup != 0) {
            $query .= ', hosts_hostgroups hg, hostgroups hg2';
        }
        if (isset($this->servicegroup) && $this->servicegroup != 0) {
            $query .= ', services_servicegroups ssg, servicegroups sg';
        }
        if ($this->criticality) {
            $query .= ', customvariables cvs ';
        }
        if (! $this->admin) {
            $query .= ', centreon_acl ';
        }
        $query .= ', services s LEFT JOIN customvariables cv ON (s.service_id = cv.service_id '
            . "AND cv.host_id = s.host_id AND cv.name = 'CRITICALITY_LEVEL') ";
        $query .= ' WHERE h.host_id = s.host_id '
            . 'AND s.enabled = 1 '
            . 'AND h.enabled = 1 '
            . 'AND h.instance_id = i.instance_id ';

        if ($this->criticality) {
            $query .= ' AND s.service_id = cvs. service_id '
                . 'AND cvs.host_id = h.host_id '
                . "AND cvs.name = 'CRITICALITY_LEVEL' "
                . 'AND cvs.value =  :criticality';
            $queryValues['criticality'] = (string) $this->criticality;
        }
        $query .= " AND h.name NOT LIKE '\_Module\_BAM%' ";

        // Search string to a host name, alias or address
        if ($this->searchHost) {
            $query .= ' AND (h.name LIKE :searchName ';
            $queryValues['searchName'] = (string) '%' . $this->searchHost . '%';
            $query .= ' OR h.alias LIKE :searchAlias ';
            $queryValues['searchAlias'] = (string) '%' . $this->searchHost . '%';
            $query .= ' OR h.address LIKE :searchAddress ) ';
            $queryValues['searchAddress'] = (string) '%' . $this->searchHost . '%';
        }
        // Search string to a service
        if ($this->search) {
            $query .= ' AND (s.description LIKE :serviceName ';
            $queryValues['serviceName'] = (string) '%' . $this->search . '%';
            $query .= ' OR s.display_name LIKE :serviceDisplay )';
            $queryValues['serviceDisplay'] = (string) '%' . $this->search . '%';
        }

        if ($this->searchOutput) {
            $query .= ' AND s.output LIKE :output ';
            $queryValues['output'] = (string) '%' . $this->searchOutput . '%';
        }

        if ($this->instance != -1 && ! empty($this->instance)) {
            $query .= ' AND h.instance_id = :instanceId ';
            $queryValues['instanceId'] = (int) $this->instance;
        }

        $q = 'ASC';
        if (isset($this->order) && strtoupper($this->order) === 'DESC') {
            $q = 'DESC';
        }
        $tabOrder = [];
        $tabOrder['criticality_id'] = " ORDER BY criticality {$q}, h.name, s.description ";
        $tabOrder['service_id'] = " ORDER BY s.service_id {$q} ";
        $tabOrder['host_name'] = " ORDER BY h.name {$q}, s.description ";
        $tabOrder['service_description'] = " ORDER BY s.description {$q}, h.name";
        $tabOrder['current_state'] = " ORDER BY s.state {$q}, h.name, s.description";
        $tabOrder['last_state_change'] = " ORDER BY s.last_state_change {$q}, h.name, s.description";
        $tabOrder['last_hard_state_change'] = " ORDER by s.last_hard_state_change {$q}, h.name, s.description";
        $tabOrder['last_check'] = " ORDER BY s.last_check {$q}, h.name, s.description";
        $tabOrder['current_attempt'] = " ORDER BY s.check_attempt {$q}, h.name, s.description";
        $tabOrder['output'] = " ORDER BY s.output {$q}, h.name, s.description";
        $tabOrder['default'] = " ORDER BY s.description {$q}, h.name";

        if ($this->viewType !== null && preg_match('/^unhandled/', $this->viewType)) {
            if (preg_match('/^unhandled_(warning|critical|unknown)$/', $this->viewType, $matches)) {
                if (isset($matches[1]) && $matches[1] == 'warning') {
                    $query .= ' AND s.state = 1 ';
                } elseif (isset($matches[1]) && $matches[1] == 'critical') {
                    $query .= ' AND s.state = 2 ';
                } elseif (isset($matches[1]) && $matches[1] == 'unknown') {
                    $query .= ' AND s.state = 3 ';
                } elseif (isset($matches[1]) && $matches[1] == 'pending') {
                    $query .= ' AND s.state = 4 ';
                } else {
                    $query .= ' AND s.state <> 0 ';
                }
            } else {
                $query .= ' AND (s.state <> 0 AND s.state <> 4) ';
            }
            $query .= ' AND s.state_type = 1';
            $query .= ' AND s.acknowledged = 0';
            $query .= ' AND s.scheduled_downtime_depth = 0';
            $query .= ' AND h.acknowledged = 0 AND h.scheduled_downtime_depth = 0 ';
        } elseif ($this->viewType == 'problems') {
            $query .= ' AND s.state <> 0 AND s.state <> 4 ';
        }

        if ($this->status == 'ok') {
            $query .= ' AND s.state = 0';
        } elseif ($this->status == 'warning') {
            $query .= ' AND s.state = 1';
        } elseif ($this->status == 'critical') {
            $query .= ' AND s.state = 2';
        } elseif ($this->status == 'unknown') {
            $query .= ' AND s.state = 3';
        } elseif ($this->status == 'pending') {
            $query .= ' AND s.state = 4';
        }

        /**
         * HostGroup Filter
         */
        if (isset($this->hostgroup) && $this->hostgroup != 0) {
            $explodedValues = '';
            foreach (explode(',', $this->hostgroup) as $hgId => $hgValue) {
                if (! is_numeric($hgValue)) {
                    throw new RestBadRequestException('Error, host group id must be numerical');
                }
                $explodedValues .= ':hostgroup' . $hgId . ',';
                $queryValues['hostgroup'][$hgId] = (int) $hgValue;
            }
            $explodedValues = rtrim($explodedValues, ',');
            $query .= ' AND hg.hostgroup_id = hg2.hostgroup_id '
                . 'AND hg.host_id = h.host_id AND hg.hostgroup_id IN (' . $explodedValues . ') ';
        }

        /**
         * ServiceGroup Filter
         */
        if (isset($this->servicegroup) && $this->servicegroup != 0) {
            $explodedValues = '';

            foreach (explode(',', $this->servicegroup) as $sgId => $sgValue) {
                if (! is_numeric($sgValue)) {
                    throw new RestBadRequestException('Error, service group id must be numerical');
                }
                $explodedValues .= ':servicegroup' . $sgId . ',';
                $queryValues['servicegroup'][$sgId] = (int) $sgValue;
            }
            $explodedValues = rtrim($explodedValues, ',');

            $query .= ' AND ssg.servicegroup_id = sg.servicegroup_id '
                . 'AND ssg.service_id = s.service_id AND ssg.servicegroup_id IN (' . $explodedValues . ') ';
        }

        /**
         * ACL activation
         */
        if (! $this->admin) {
            $query .= ' AND h.host_id = centreon_acl.host_id '
                . 'AND s.service_id = centreon_acl.service_id '
                . 'AND group_id IN (' . $this->aclObj->getAccessGroupsString() . ') ';
        }

        (isset($tabOrder[$this->sortType])) ? $query .= $tabOrder[$this->sortType] : $query .= $tabOrder['default'];

        $query .= ' LIMIT :offset,:limit';
        $queryValues['offset'] = (int) ($this->number * $this->limit);
        $queryValues['limit'] = (int) $this->limit;

        $stmt = $this->realTimeDb->prepare($query);

        if (isset($queryValues['criticality'])) {
            $stmt->bindParam(':criticality', $queryValues['criticality'], PDO::PARAM_INT);
        }

        if (isset($queryValues['searchName'])) {
            $stmt->bindParam(':searchName', $queryValues['searchName'], PDO::PARAM_STR);
            $stmt->bindParam(':searchAlias', $queryValues['searchAlias'], PDO::PARAM_STR);
            $stmt->bindParam(':searchAddress', $queryValues['searchAddress'], PDO::PARAM_STR);
        }

        if (isset($queryValues['serviceName'])) {
            $stmt->bindParam(':serviceName', $queryValues['serviceName'], PDO::PARAM_STR);
            $stmt->bindParam(':serviceDisplay', $queryValues['serviceDisplay'], PDO::PARAM_STR);
        }

        if (isset($queryValues['output'])) {
            $stmt->bindParam(':output', $queryValues['output'], PDO::PARAM_STR);
        }

        if (isset($queryValues['instanceId'])) {
            $stmt->bindParam(':instanceId', $queryValues['instanceId'], PDO::PARAM_INT);
        }

        if (isset($queryValues['hostgroup'])) {
            foreach ($queryValues['hostgroup'] as $hgId => $hgValue) {
                $stmt->bindValue(':hostgroup' . $hgId, $hgValue, PDO::PARAM_INT);
            }
        }

        if (isset($queryValues['servicegroup'])) {
            foreach ($queryValues['servicegroup'] as $hgId => $hgValue) {
                $stmt->bindValue(':servicegroup' . $hgId, $hgValue, PDO::PARAM_INT);
            }
        }

        $stmt->bindParam(':offset', $queryValues['offset'], PDO::PARAM_INT);
        $stmt->bindParam(':limit', $queryValues['limit'], PDO::PARAM_INT);

        $stmt->execute();

        $dataList = [];
        while ($data = $stmt->fetch()) {
            if (isset($data['criticality'], $this->criticalityList[$data['criticality']])) {
                $data['criticality'] = $this->criticalityList[$data['criticality']];
            }
            $dataList[] = $data;
        }

        return $dataList;
    }

    /**
     * @return array
     */
    protected function getFieldContent()
    {
        $tab = explode(',', $this->arguments['fields']);

        $fieldList = [];
        foreach ($tab as $key) {
            $fieldList[$key] = 1;
        }

        return $fieldList;
    }

    /**
     * @throws RestBadRequestException
     * @return void
     */
    protected function setServiceFilters()
    {
        // Pagination Elements
        $this->limit = $this->arguments['limit'] ?? 30;
        $this->number = $this->arguments['number'] ?? 0;
        if (! is_numeric($this->number) || ! is_numeric($this->limit)) {
            throw new RestBadRequestException('Error, limit must be numerical');
        }

        // Filters
        if (isset($this->arguments['status'])) {
            $statusList = ['ok', 'warning', 'critical', 'unknown', 'pending', 'all'];
            if (in_array(strtolower($this->arguments['status']), $statusList)) {
                $this->status = $this->arguments['status'];
            } else {
                throw new RestBadRequestException('Error, bad status parameter');
            }
        } else {
            $this->status = null;
        }
        $this->hostgroup = $this->arguments['hostgroup'] ?? null;
        $this->servicegroup = $this->arguments['servicegroup'] ?? null;
        $this->search = $this->arguments['search'] ?? null;
        $this->searchHost = $this->arguments['searchHost'] ?? null;
        $this->searchOutput = $this->arguments['searchOutput'] ?? null;
        $this->instance = $this->arguments['instance'] ?? null;
        // set criticality
        $this->criticality = $this->arguments['criticality'] ?? null;

        // view properties
        $this->viewType = $this->arguments['viewType'] ?? null;
        $this->sortType = $this->arguments['sortType'] ?? null;
        $this->order = $this->arguments['order'] ?? null;
    }

    /**
     * @return void
     */
    protected function setServiceFieldList()
    {
        $fields = [];

        if (! isset($this->arguments['fields'])) {
            $fields['h.host_id'] = 'host_id';
            $fields['h.name'] = 'name';
            $fields['s.description'] = 'description';
            $fields['s.service_id'] = 'service_id';
            $fields['s.state'] = 'state';
            $fields['s.state_type'] = 'state_type';
            $fields['s.output'] = 'output';
            $fields['s.perfdata'] = 'perfdata';
            $fields['s.max_check_attempts'] = 'max_check_attempts';
            $fields['s.check_attempt'] = 'check_attempt';
            $fields['s.last_check'] = 'last_check';
            $fields['s.last_state_change'] = 'last_state_change';
            $fields['s.last_hard_state_change'] = 'last_hard_state_change';
            $fields['s.acknowledged'] = 'acknowledged';
            $fields['cv.value as criticality'] = 'criticality';
        } else {
            $tab = explode(',', $this->arguments['fields']);

            $fieldList = [];
            foreach ($tab as $key) {
                $fieldList[trim($key)] = 1;
            }

            // hosts informations
            if (isset($fieldList['host_id'])) {
                $fields['h.host_id'] = 'host_id';
            }
            if (isset($fieldList['host_name'])) {
                $fields['h.name as host_name'] = 'host_name';
            }
            if (isset($fieldList['host_alias'])) {
                $fields['h.alias as host_alias'] = 'host_alias';
            }
            if (isset($fieldList['host_address'])) {
                $fields['h.address as host_address'] = 'host_address';
            }
            if (isset($fieldList['host_state'])) {
                $fields['h.state as host_state'] = 'host_state';
            }
            if (isset($fieldList['host_state_type'])) {
                $fields['h.state_type as host_state_type'] = 'host_state_type';
            }
            if (isset($fieldList['host_output'])) {
                $fields['h.output as host_output'] = 'host_output';
            }
            if (isset($fieldList['host_last_check'])) {
                $fields['h.last_check as host_last_check'] = 'host_last_check';
            }
            if (isset($fieldList['host_next_check'])) {
                $fields['h.next_check as host_next_check'] = 'host_next_check';
            }
            if (isset($fieldList['host_acknowledged'])) {
                $fields['h.acknowledged as host_acknowledged'] = 'host_acknowledged';
            }
            if (isset($fieldList['instance'])) {
                $fields['i.name as instance_name'] = 'instance';
            }
            if (isset($fieldList['instance_id'])) {
                $fields['i.instance_id as instance_id'] = 'instance_id';
            }
            if (isset($fieldList['host_action_url'])) {
                $fields['h.action_url as host_action_url'] = 'host_action_url';
            }
            if (isset($fieldList['host_notes_url'])) {
                $fields['h.notes_url as host_notes_url'] = 'host_notes_url';
            }
            if (isset($fieldList['host_notes'])) {
                $fields['h.notes as host_notes'] = 'host_notes';
            }
            if (isset($fieldList['host_icon_image'])) {
                $fields['h.icon_image as host_icon_image'] = 'host_icon_image';
            }

            // services informations
            if (isset($fieldList['description'])) {
                $fields['s.description'] = 'description';
            }
            if (isset($fieldList['state'])) {
                $fields['s.state'] = 'state';
            }
            if (isset($fieldList['state_type'])) {
                $fields['s.state_type'] = 'state_type';
            }
            if (isset($fieldList['service_id'])) {
                $fields['s.service_id'] = 'service_id';
            }
            if (isset($fieldList['output'])) {
                $fields['s.output'] = 'output';
            }
            if (isset($fieldList['perfdata'])) {
                $fields['s.perfdata'] = 'perfdata';
            }
            if (isset($fieldList['current_attempt'])) {
                $fields['s.check_attempt as current_attempt'] = 'current_attempt';
            }
            if (isset($fieldList['last_update'])) {
                $fields['s.last_update'] = 'last_update';
            }
            if (isset($fieldList['last_state_change'])) {
                $fields['s.last_state_change'] = 'last_state_change';
            }
            if (isset($fieldList['last_hard_state_change'])) {
                $fields['s.last_hard_state_change'] = 'last_hard_state_change';
            }
            if (isset($fieldList['last_state_change'])) {
                $fields['s.last_state_change'] = 'last_state_change';
            }
            if (isset($fieldList['last_check'])) {
                $fields['s.last_check'] = 'last_check';
            }
            if (isset($fieldList['next_check'])) {
                $fields['s.next_check'] = 'next_check';
            }
            if (isset($fieldList['max_check_attempts'])) {
                $fields['s.max_check_attempts'] = 'max_check_attempts';
            }
            if (isset($fieldList['notes'])) {
                $fields['s.notes'] = 'notes';
            }
            if (isset($fieldList['notes_url'])) {
                $fields['s.notes_url'] = 'notes_url';
            }
            if (isset($fieldList['action_url'])) {
                $fields['s.action_url'] = 'action_url';
            }
            if (isset($fieldList['icon_image'])) {
                $fields['s.icon_image'] = 'icon_image';
            }
            if (isset($fieldList['display_name'])) {
                $fields['s.display_name'] = 'display_name';
            }
            if (isset($fieldList['notify'])) {
                $fields['s.notify'] = 'notify';
            }
            if (isset($fieldList['acknowledged'])) {
                $fields['s.acknowledged'] = 'acknowledged';
            }
            if (isset($fieldList['passive_checks'])) {
                $fields['s.passive_checks'] = 'passive_checks';
            }
            if (isset($fieldList['active_checks'])) {
                $fields['s.active_checks'] = 'active_checks';
            }
            if (isset($fieldList['event_handler_enabled'])) {
                $fields['s.event_handler_enabled'] = 'event_handler_enabled';
            }
            if (isset($fieldList['flapping'])) {
                $fields['s.flapping'] = 'flapping';
            }
            if (isset($fieldList['scheduled_downtime_depth'])) {
                $fields['s.scheduled_downtime_depth'] = 'scheduled_downtime_depth';
            }
            if (isset($fieldList['flap_detection'])) {
                $fields['s.flap_detection'] = 'flap_detection';
            }
            if (isset($fieldList['criticality'])) {
                $fields['cv.value as criticality'] = 'criticality';
            }
        }

        $sortColumns = [
            'criticality_id' => ['criticality', 'h.name', 's.description'],
            'service_id' => ['s.service_id'],
            'host_name' => ['h.name', 's.description'],
            'service_description' => ['s.description', 'h.name'],
            'current_state' => ['s.state', 'h.name', 's.description'],
            'last_state_change' => ['s.last_state_change', 'h.name', 's.description'],
            'last_hard_state_change' => ['s.last_hard_state_change', 'h.name', 's.description'],
            'last_check' => ['s.last_check', 'h.name', 's.description'],
            'current_attempt' => ['s.check_attempt', 'h.name', 's.description'],
            'output' => ['s.output', 'h.name', 's.description'],
            'default' => ['s.description', 'h.name'],
        ];
        $sortType = $this->sortType ?? 'default';
        $columnsToAdd = $sortColumns[$sortType] ?? $sortColumns['default'];
        foreach ($columnsToAdd as $col) {
            // If column isn't already in the SELECT, add it
            $alreadyPresent = false;
            foreach ($fields as $key => $alias) {
                if (str_contains($key, $col)) {
                    $alreadyPresent = true;
                    break;
                }
            }
            if (! $alreadyPresent) {
                $fields[$col] = $col;
            }
        }

        // Build Field List
        $this->fieldList = '';
        foreach ($fields as $key => $value) {
            if ($this->fieldList != '') {
                $this->fieldList .= ', ';
            }
            $this->fieldList .= $key;
        }
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function getCriticality()
    {
        $this->criticalityList = [];

        $sql = 'SELECT `sc_id`, `sc_name`, `level`, `icon_id`, `sc_description` FROM `service_categories` '
            . 'WHERE `level` IS NOT NULL ORDER BY `level` DESC';
        $res = $this->pearDB->query($sql);
        while ($row = $res->fetch()) {
            $this->criticalityList[$row['sc_name']] = $row;
        }
    }
}
