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

namespace CentreonClapi;

use Centreon_Object_Downtime;
use Centreon_Object_Host;
use Centreon_Object_Host_Group;
use Centreon_Object_Service_Group;
use PDOException;
use Pimple\Container;

require_once 'centreonObject.class.php';
require_once 'centreonHost.class.php';
require_once 'centreonService.class.php';
require_once 'Centreon/Object/Downtime/Downtime.php';
require_once 'Centreon/Object/Host/Host.php';
require_once 'Centreon/Object/Host/Group.php';
require_once 'Centreon/Object/Service/Group.php';
require_once 'Centreon/Object/Relation/Downtime/Host.php';
require_once 'Centreon/Object/Relation/Downtime/Hostgroup.php';
require_once 'Centreon/Object/Relation/Downtime/Servicegroup.php';

/**
 * Class
 *
 * @class CentreonDowntime
 * @package CentreonClapi
 * @description Class for managing recurring downtime objects
 */
class CentreonDowntime extends CentreonObject
{
    public const ORDER_UNIQUENAME = 0;
    public const ORDER_ALIAS = 1;

    /** @var string[] */
    public static $aDepends = ['SERVICE', 'HOST'];

    /** @var int[] */
    protected $weekDays = ['monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6, 'sunday' => 7];

    /** @var CentreonService */
    protected $serviceObj;

    /** @var string[] */
    protected $availableCycles = ['first', 'second', 'third', 'fourth', 'last'];

    /**
     * CentreonDowntime constructor
     *
     * @param Container $dependencyInjector
     *
     * @throws PDOException
     */
    public function __construct(Container $dependencyInjector)
    {
        parent::__construct($dependencyInjector);
        $this->serviceObj = new CentreonService($dependencyInjector);
        $this->object = new Centreon_Object_Downtime($dependencyInjector);
        $this->action = 'DOWNTIME';
        $this->insertParams = ['dt_name', 'dt_description'];
        $this->exportExcludedParams = array_merge(
            $this->insertParams,
            [$this->object->getPrimaryKey()]
        );
        $this->nbOfCompulsoryParams = count($this->insertParams);
        $this->activateField = 'dt_activate';
    }

    /**
     * @param null $parameters
     * @param array $filters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function show($parameters = null, $filters = []): void
    {
        $filters = [];
        $filter = [];
        if (isset($parameters) && $parameters !== '') {
            $filter = explode(';', $parameters);
            $filters = [$this->object->getUniqueLabelField() => '%' . $filter[0] . '%'];
        }
        $params = ['dt_id', 'dt_name', 'dt_description', 'dt_activate'];
        $paramString = str_replace('dt_', '', implode($this->delim, $params));
        $elements = $this->object->getList($params, -1, 0, null, null, $filters);

        if (empty($elements)) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND);
        }

        if ($filter === []) {
            echo $paramString . "\n";
            $filterList = '';
            foreach ($elements as $element) {
                echo implode($this->delim, $element) . "\n";
            }
        } else {
            if (isset($filter[1])) {
                switch (strtolower($filter[1])) {
                    case 'host':
                        $paramString .= ";hosts\n";
                        break;
                    case 'hg':
                        $paramString .= ";host groups\n";
                        break;
                    case 'service':
                        $paramString .= ";services\n";
                        break;
                    case 'sg':
                        $paramString .= ";service groups\n";
                        break;
                    default:
                        throw new CentreonClapiException(self::UNKNOWNPARAMETER);
                }
            } else {
                $paramString .= ";hosts;host groups;services;service groups\n";
            }
            echo $paramString;
            foreach ($elements as $element) {
                if (isset($filter[1])) {
                    switch (strtolower($filter[1])) {
                        case 'host':
                            $filterList = $this->listHosts($element['dt_id']);
                            break;
                        case 'hg':
                            $filterList = $this->listHostGroups($element['dt_id']);
                            break;
                        case 'service':
                            $filterList = $this->listServices($element['dt_id']);
                            break;
                        case 'sg':
                            $filterList = $this->listServiceGroups($element['dt_id']);
                            break;
                        default:
                            throw new CentreonClapiException(self::UNKNOWNPARAMETER);
                    }
                } else {
                    $filterList = $this->listResources($element['dt_id']);
                }
                if (! empty($filterList)) {
                    echo implode($this->delim, $element) . ';' . $filterList . "\n";
                }
            }
        }
    }

    /**
     * @param null $parameters
     * @throws CentreonClapiException
     * @return void
     */
    public function initInsertParameters($parameters = null): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < $this->nbOfCompulsoryParams) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $addParams = [];
        $addParams[$this->object->getUniqueLabelField()] = $params[self::ORDER_UNIQUENAME];
        $addParams['dt_description'] = $params[self::ORDER_ALIAS];
        $this->params = array_merge($this->params, $addParams);
        $this->checkParameters();
    }

    /**
     * @param null $parameters
     * @throws CentreonClapiException
     * @return array
     */
    public function initUpdateParameters($parameters = null)
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < self::NB_UPDATE_PARAMS) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }

        $objectId = $this->getObjectId($params[self::ORDER_UNIQUENAME]);
        if ($objectId != 0) {
            if (! preg_match('/^dt_/', $params[1])) {
                $params[1] = 'dt_' . $params[1];
            }
            $updateParams = [$params[1] => $params[2]];
            $updateParams['objectId'] = $objectId;

            return $updateParams;
        }

        throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
    }

    /**
     * List periods
     *
     * @param string $parameters | downtime name
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function listperiods($parameters): void
    {
        $dtId = $this->getObjectId($parameters);
        $rows = $this->getPeriods($dtId);

        echo
            implode(
                $this->delim,
                ['position', 'start time', 'end time', 'fixed', 'duration', 'day of week', 'day of month', 'month cycle']
            ) . "\n";
        $pos = 1;
        foreach ($rows as $row) {
            unset($row['dt_id']);
            echo $pos . $this->delim;
            echo implode($this->delim, $row) . "\n";
            $pos++;
        }
    }

    /**
     * Add weekly period
     *
     * @param string $parameters | downtime_name;start;end;fixed;duration;monday...sunday
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addweeklyperiod($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 6) {
            throw new CentreonClapiException('Incorrect number of parameters');
        }
        if (! is_numeric($tmp[3])) {
            throw new CentreonClapiException('Incorrect fixed parameters');
        }
        if ($tmp[3] == 0 && ! is_numeric($tmp[4])) {
            throw new CentreonClapiException('Incorrect duration parameters: mandatory for flexible downtimes');
        }

        $p = [];
        $p[':dt_id'] = $this->getObjectId($tmp[0]);
        $p[':start_time'] = $tmp[1];
        $p[':end_time'] = $tmp[2];
        $p[':fixed'] = $tmp[3];
        $p[':duration'] = $tmp[3] == 0 ? $tmp[4] : null;
        $daysOfWeek = explode(',', strtolower($tmp[5]));
        $days = [];
        foreach ($daysOfWeek as $dayOfWeek) {
            if (! isset($this->weekDays[$dayOfWeek]) && ! in_array($dayOfWeek, $this->weekDays)) {
                throw new CentreonClapiException(sprintf('Invalid period format %s', $dayOfWeek));
            }
            $days[] = is_numeric($dayOfWeek) ? $dayOfWeek : $this->weekDays[$dayOfWeek];
        }
        $p[':day_of_week'] = implode(',', $days);
        $p[':day_of_month'] = null;
        $p[':month_cycle'] = 'all';
        $this->insertPeriod($p);
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addmonthlyperiod($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 6) {
            throw new CentreonClapiException('Incorrect number of parameters');
        }
        if (! is_numeric($tmp[3])) {
            throw new CentreonClapiException('Incorrect fixed parameters');
        }
        if (! is_numeric($tmp[4])) {
            throw new CentreonClapiException('Incorrect duration parameters');
        }

        $p = [];
        $p[':dt_id'] = $this->getObjectId($tmp[0]);
        $p[':start_time'] = $tmp[1];
        $p[':end_time'] = $tmp[2];
        $p[':fixed'] = $tmp[3];
        $p[':duration'] = $tmp[4];
        $p[':day_of_month'] = $tmp[5];
        $p[':day_of_week'] = null;
        $p[':month_cycle'] = 'none';
        $this->insertPeriod($p);
    }

    /**
     * Add specific period
     *
     * @param string $parameters | downtime_name;start;end;fixed;duration;monday...sunday;first,last
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addspecificperiod($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 7) {
            throw new CentreonClapiException('Incorrect number of parameters');
        }
        if (! is_numeric($tmp[3])) {
            throw new CentreonClapiException('Incorrect fixed parameters');
        }
        if (! is_numeric($tmp[4])) {
            throw new CentreonClapiException('Incorrect duration parameters');
        }
        $p = [];
        $p[':dt_id'] = $this->getObjectId($tmp[0]);
        $p[':start_time'] = $tmp[1];
        $p[':end_time'] = $tmp[2];
        $p[':fixed'] = $tmp[3];
        $p[':duration'] = $tmp[4];
        $dayOfWeek = strtolower($tmp[5]);
        if (! isset($this->weekDays[$dayOfWeek]) && ! in_array($dayOfWeek, $this->weekDays)) {
            throw new CentreonClapiException(sprintf('Invalid period format %s', $dayOfWeek));
        }
        $p[':day_of_week'] = is_numeric($dayOfWeek) ? $dayOfWeek : $this->weekDays[$dayOfWeek];
        $p[':day_of_month'] = null;

        $cycle = strtolower($tmp[6]);

        if (! in_array($cycle, $this->availableCycles)) {
            throw new CentreonClapiException(
                sprintf('Invalid cycle format %s. Must be "first", "second, "third", "fourth" or "last"', $cycle)
            );
        }

        $p[':month_cycle'] = $cycle;
        $this->insertPeriod($p);
    }

    /**
     * Delete period from downtime
     *
     * @param string $parameters | downtime_name;position to delete
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function delperiod($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Incorrect number of parameters');
        }
        $sql = 'DELETE FROM downtime_period
            WHERE dt_id = ?
            AND dtp_start_time = ?
            AND dtp_end_time = ?
            AND dtp_fixed = ?
            AND dtp_duration = ?
            AND dtp_day_of_week = ?
            AND dtp_day_of_month = ?
            AND dtp_month_cycle = ?';

        $period = $this->getPeriods($this->getObjectId($tmp[0]), $tmp[1]);
        $periodParams = [];
        foreach ($period as $k => $v) {
            if ($v == '') {
                $sql = str_replace("{$k} = ?", "{$k} IS NULL", $sql);
            } else {
                $periodParams[] = $v;
            }
        }
        $this->db->query($sql, $periodParams);
    }

    /**
     * @param $downtimeId
     *
     * @throws PDOException
     * @return string
     */
    public function listHosts($downtimeId)
    {
        // hosts
        $sql = 'SELECT host_name
            FROM downtime_host_relation dhr, host h
            WHERE h.host_id = dhr.host_host_id
            AND dhr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $hosts = [];
        foreach ($rows as $row) {
            $hosts[] = $row['host_name'];
        }

        return implode('|', $hosts);
    }

    /**
     * @param $downtimeId
     *
     * @throws PDOException
     * @return string
     */
    public function listHostGroups($downtimeId)
    {
        // host groups
        $sql = 'SELECT hg_name
            FROM downtime_hostgroup_relation dhr, hostgroup hg
            WHERE hg.hg_id = dhr.hg_hg_id
            AND dhr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $hostgroups = [];
        foreach ($rows as $row) {
            $hostgroups[] = $row['hg_name'];
        }

        return implode('|', $hostgroups);
    }

    /**
     * @param $downtimeId
     *
     * @throws PDOException
     * @return string
     */
    public function listServices($downtimeId)
    {
        // services
        $sql = 'SELECT host_name, service_description
            FROM downtime_service_relation dsr, host h, service s
            WHERE h.host_id = dsr.host_host_id
            AND dsr.service_service_id = s.service_id
            AND dsr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $services = [];
        foreach ($rows as $row) {
            $services[] = $row['host_name'] . ',' . $row['service_description'];
        }

        return implode('|', $services);
    }

    /**
     * @param $downtimeId
     *
     * @throws PDOException
     * @return string
     */
    public function listServiceGroups($downtimeId)
    {
        // service groups
        $sql = 'SELECT sg_name
            FROM downtime_servicegroup_relation dsr, servicegroup sg
            WHERE sg.sg_id = dsr.sg_sg_id
            AND dsr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $servicegroups = [];
        foreach ($rows as $row) {
            $servicegroups[] = $row['sg_name'];
        }

        return implode('|', $servicegroups);
    }

    /**
     * @param $downtimeId
     *
     * @throws PDOException
     * @return string
     */
    public function listResources($downtimeId)
    {
        // hosts
        $sql = 'SELECT host_name
            FROM downtime_host_relation dhr, host h
            WHERE h.host_id = dhr.host_host_id
            AND dhr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $hosts = [];
        foreach ($rows as $row) {
            $hosts[] = $row['host_name'];
        }

        // host groups
        $sql = 'SELECT hg_name
            FROM downtime_hostgroup_relation dhr, hostgroup hg
            WHERE hg.hg_id = dhr.hg_hg_id
            AND dhr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $hostgroups = [];
        foreach ($rows as $row) {
            $hostgroups[] = $row['hg_name'];
        }

        // services
        $sql = 'SELECT host_name, service_description
            FROM downtime_service_relation dsr, host h, service s
            WHERE h.host_id = dsr.host_host_id
            AND dsr.service_service_id = s.service_id
            AND dsr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $services = [];
        foreach ($rows as $row) {
            $services[] = $row['host_name'] . ',' . $row['service_description'];
        }

        // service groups
        $sql = 'SELECT sg_name
            FROM downtime_servicegroup_relation dsr, servicegroup sg
            WHERE sg.sg_id = dsr.sg_sg_id
            AND dsr.dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();
        $servicegroups = [];
        foreach ($rows as $row) {
            $servicegroups[] = $row['sg_name'];
        }

        return implode('|', $hosts) . $this->delim . implode('|', $hostgroups) . $this->delim
            . implode('|', $services) . $this->delim . implode('|', $servicegroups);
    }

    /**
     * Add host to downtime
     *
     * @param string $parameters | downtime name; host names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addhost($parameters): void
    {
        $object = new Centreon_Object_Host($this->dependencyInjector);
        $this->addGenericRelation($parameters, $object, 'downtime_host_relation', 'host_host_id');
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function sethost($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // delete all host relationships
        $downtimeId = $this->getObjectId($tmp[0]);
        $this->db->query('DELETE FROM downtime_host_relation WHERE dt_id = ?', [$downtimeId]);

        $this->addhost($parameters);
    }

    /**
     * Delete host from downtime
     *
     * @param string $parameters | downtime name; host names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function delhost($parameters): void
    {
        $object = new Centreon_Object_Host($this->dependencyInjector);
        $this->delGenericRelation($parameters, $object, 'downtime_host_relation', 'host_host_id');
    }

    /**
     * Add host group to downtime
     *
     * @param string $parameters | downtime name; host group names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addhostgroup($parameters): void
    {
        $object = new Centreon_Object_Host_Group($this->dependencyInjector);
        $this->addGenericRelation($parameters, $object, 'downtime_hostgroup_relation', 'hg_hg_id');
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function sethostgroup($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // delete all host group relationships
        $downtimeId = $this->getObjectId($tmp[0]);
        $this->db->query('DELETE FROM downtime_hostgroup_relation WHERE dt_id = ?', [$downtimeId]);

        $this->addhostgroup($parameters);
    }

    /**
     * Delete host groups
     *
     * @param string $parameters | downtime name; host group names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function delhostgroup($parameters): void
    {
        $object = new Centreon_Object_Host_Group($this->dependencyInjector);
        $this->delGenericRelation($parameters, $object, 'downtime_hostgroup_relation', 'hg_hg_id');
    }

    /**
     * Add service to downtime
     *
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addservice($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // init var
        $downtimeId = $this->getObjectId($tmp[0]);
        if ($downtimeId == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $tmp[0]);
        }

        $resources = explode('|', $tmp[1]);

        // retrieve object ids
        $objectIds = [];
        foreach ($resources as $resource) {
            $tmp = explode(',', $resource);
            if (count($tmp) != 2) {
                throw new CentreonClapiException(sprintf('Wrong format for service %s', $resource));
            }
            $host = $tmp[0];
            $service = $tmp[1];

            $ids = $this->serviceObj->getHostAndServiceId($host, $service);

            // object does not exist
            if (! count($ids)) {
                throw new CentreonClapiException(sprintf('Could not find service %s on host %s', $service, $host));
            }

            // checks whether or not relationship already exists
            $sql = 'SELECT *
                FROM downtime_service_relation
                WHERE dt_id = ?
                AND host_host_id = ?
                AND service_service_id = ?';
            $stmt = $this->db->query($sql, [$downtimeId, $ids[0], $ids[1]]);
            if ($stmt->rowCount()) {
                throw new CentreonClapiException(
                    sprintf(
                        'Relationship with %s / %s already exists',
                        $host,
                        $service
                    )
                );
            }

            $objectIds[] = $ids;
        }

        // insert relationship
        $sql = 'INSERT INTO downtime_service_relation (dt_id, host_host_id, service_service_id) VALUES (?, ?, ?)';

        foreach ($objectIds as $id) {
            $this->db->query($sql, [$downtimeId, $id[0], $id[1]]);
        }
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function setservice($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // delete all service relationships
        $downtimeId = $this->getObjectId($tmp[0]);
        $this->db->query('DELETE FROM downtime_service_relation WHERE dt_id = ?', [$downtimeId]);

        $this->addservice($parameters);
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function delservice($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // init var
        $downtimeId = $this->getObjectId($tmp[0]);
        $resources = explode('|', $tmp[1]);

        // retrieve object ids
        $objectIds = [];
        foreach ($resources as $resource) {
            $tmp = explode(',', $resource);
            if (count($tmp) != 2) {
                throw new CentreonClapiException(sprintf('Wrong format for service %s', $resource));
            }
            $host = $tmp[0];
            $service = $tmp[1];

            $ids = $this->serviceObj->getHostAndServiceId($host, $service);

            // object does not exist
            if (! count($ids)) {
                throw new CentreonClapiException(sprintf('Could not find service %s on host %s', $service, $host));
            }

            // checks whether or not relationship already exists
            $sql = 'SELECT *
                FROM downtime_service_relation
                WHERE dt_id = ?
                AND host_host_id = ?
                AND service_service_id = ?';
            $stmt = $this->db->query($sql, [$downtimeId, $ids[0], $ids[1]]);
            if (! $stmt->rowCount()) {
                throw new CentreonClapiException(
                    sprintf(
                        'Relationship with %s / %s does not exist',
                        $host,
                        $service
                    )
                );
            }

            $objectIds[] = $ids;
        }

        // delete relationship
        $sql = 'DELETE FROM downtime_service_relation
            WHERE dt_id = ?
            AND host_host_id = ?
            AND service_service_id = ?';
        foreach ($objectIds as $id) {
            $this->db->query($sql, [$downtimeId, $id[0], $id[1]]);
        }
    }

    /**
     * Add service group to downtime
     *
     * @param string $parameters | downtime name; service group names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function addservicegroup($parameters): void
    {
        $object = new Centreon_Object_Service_Group($this->dependencyInjector);
        $this->addGenericRelation($parameters, $object, 'downtime_servicegroup_relation', 'sg_sg_id');
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function setservicegroup($parameters): void
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // delete all service group relationships
        $downtimeId = $this->getObjectId($tmp[0]);
        $this->db->query('DELETE FROM downtime_servicegroup_relation WHERE dt_id = ?', [$downtimeId]);

        $this->addservicegroup($parameters);
    }

    /**
     * Delete service group from downtime
     *
     * @param string $parameters | downtime name; service group names separated by "|" character
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function delservicegroup($parameters): void
    {
        $object = new Centreon_Object_Service_Group($this->dependencyInjector);
        $this->delGenericRelation($parameters, $object, 'downtime_servicegroup_relation', 'sg_sg_id');
    }

    /**
     * Export
     *
     * @param null $filterName
     *
     * @throws PDOException
     * @return false|void
     */
    public function export($filterName = null)
    {
        // generic add & setparam
        if (! parent::export($filterName)) {
            return false;
        }

        // handle host relationships
        $this->exportHostRel();

        // handle hostgroup relationships
        $this->exportHostgroupRel();

        // handle service relationships
        $this->exportServiceRel();

        // handle servicegroup relationships
        $this->exportServicegroupRel();

        // handle periods
        $this->exportPeriods();
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function exportPeriods()
    {
        $sql = 'SELECT dt_name, dtp_start_time, dtp_end_time, dtp_fixed, dtp_duration,
            dtp_day_of_week, dtp_day_of_month, dtp_month_cycle
            FROM downtime d, downtime_period p
            WHERE d.dt_id = p.dt_id';
        $stmt = $this->db->query($sql);
        $rows = $stmt->fetchAll();
        foreach ($rows as $row) {
            $periodType = null;
            $extraData = [];
            $row['dtp_start_time'] = preg_replace('/:00$/', '', $row['dtp_start_time']);
            $row['dtp_end_time'] = preg_replace('/:00$/', '', $row['dtp_end_time']);
            if (! $row['dtp_day_of_month'] && $row['dtp_month_cycle'] == 'all') { // weekly
                $periodType = 'ADDWEEKLYPERIOD';
                $extraData[] = $row['dtp_day_of_week'];
            } elseif (! $row['dtp_day_of_week'] && $row['dtp_month_cycle'] == 'none') { // monthly
                $periodType = 'ADDMONTHLYPERIOD';
                $extraData[] = $row['dtp_day_of_month'];
            } elseif ($row['dtp_month_cycle'] == 'last' || $row['dtp_month_cycle'] == 'first') { // specific
                $periodType = 'ADDSPECIFICPERIOD';
                $extraData[] = $row['dtp_day_of_week'];
                $extraData[] = $row['dtp_month_cycle'];
            }
            if (! is_null($periodType)) {
                echo
                    implode(
                        $this->delim,
                        array_merge(
                            [$this->action, $periodType, $row['dt_name'], $row['dtp_start_time'], $row['dtp_end_time'], $row['dtp_fixed'], $row['dtp_duration']],
                            $extraData
                        )
                    ) . "\n";
            }
        }
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function exportHostRel()
    {
        $sql = 'SELECT dt_name, host_name as object_name
            FROM downtime d, host o, downtime_host_relation rel
            WHERE d.dt_id = rel.dt_id
            AND rel.host_host_id = o.host_id';
        $this->exportGenericRel('ADDHOST', $sql);
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function exportHostgroupRel()
    {
        $sql = 'SELECT dt_name, hg_name as object_name
            FROM downtime d, hostgroup o, downtime_hostgroup_relation rel
            WHERE d.dt_id = rel.dt_id
            AND rel.hg_hg_id = o.hg_id';
        $this->exportGenericRel('ADDHOSTGROUP', $sql);
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function exportServiceRel()
    {
        $sql = "SELECT dt_name, CONCAT_WS(',', host_name, service_description) as object_name
            FROM downtime d, host h, service s, downtime_service_relation rel
            WHERE d.dt_id = rel.dt_id
            AND rel.host_host_id = h.host_id
            AND rel.service_service_id = s.service_id";
        $this->exportGenericRel('ADDSERVICE', $sql);
    }

    /**
     * @throws PDOException
     * @return void
     */
    protected function exportServicegroupRel()
    {
        $sql = 'SELECT dt_name, sg_name as object_name
            FROM downtime d, servicegroup o, downtime_servicegroup_relation rel
            WHERE d.dt_id = rel.dt_id
            AND rel.sg_sg_id = o.sg_id';
        $this->exportGenericRel('ADDSERVICEGROUP', $sql);
    }

    /**
     * @param string $actionType | addhost, addhostgroup, addservice or addservicegroup
     * @param string $sql | query
     *
     * @throws PDOException
     */
    protected function exportGenericRel($actionType, $sql)
    {
        $stmt = $this->db->query($sql);
        $rows = $stmt->fetchAll();
        foreach ($rows as $row) {
            echo
                implode(
                    $this->delim,
                    [$this->action, $actionType, $row['dt_name'], $row['object_name']]
                ) . "\n";
        }
    }

    /**
     * Add period
     *
     * @param $p
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    protected function insertPeriod($p)
    {
        // check time periods
        $p[':start_time'] .= ':00';
        $p[':end_time'] .= ':00';
        if (! preg_match('/^(\d+):(\d+):00$/', $p[':start_time'])) {
            throw new CentreonClapiException(sprintf('Invalid start time %s', $p[':start_time']));
        }
        if (! preg_match('/^(\d+):(\d+):00$/', $p[':end_time'])) {
            throw new CentreonClapiException(sprintf('Invalid end time %s', $p[':end_time']));
        }

        // handle fixed / duration
        if ($p[':fixed']) {
            $p[':duration'] = null;
        }

        // delete duplicate period
        $sql = 'DELETE FROM downtime_period
            WHERE dt_id = :dt_id
            AND dtp_start_time = :start_time
            AND dtp_end_time = :end_time
            AND dtp_fixed = :fixed
            AND dtp_duration = :duration
            AND dtp_day_of_week = :day_of_week
            AND dtp_day_of_month = :day_of_month
            AND dtp_month_cycle = :month_cycle';
        $delParams = [];
        foreach ($p as $k => $v) {
            if ($v == '') {
                $sql = str_replace("= {$k}", 'IS NULL', $sql);
            } else {
                $delParams[$k] = $v;
            }
        }
        $this->db->query($sql, $delParams);

        $sql = 'INSERT INTO downtime_period
            (dt_id, dtp_start_time, dtp_end_time, dtp_fixed, dtp_duration,
            dtp_day_of_week, dtp_day_of_month, dtp_month_cycle)
            VALUES (:dt_id, :start_time, :end_time, :fixed, :duration,
                :day_of_week, :day_of_month, :month_cycle)';
        $this->db->query($sql, $p);
    }

    /**
     * Get preiods from downtime id
     *
     * @param $downtimeId
     * @param null $position
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return mixed
     */
    protected function getPeriods($downtimeId, $position = null)
    {
        $sql = 'SELECT dt_id, dtp_start_time, dtp_end_time, dtp_fixed, dtp_duration,
            dtp_day_of_week, dtp_day_of_month, dtp_month_cycle
            FROM downtime_period
            WHERE dt_id = ?';
        $stmt = $this->db->query($sql, [$downtimeId]);
        $rows = $stmt->fetchAll();

        if (! is_null($position)) {
            $cur = 1;
            foreach ($rows as $row) {
                if ($cur == $position) {
                    return $row;
                }
                $cur++;
            }

            throw new CentreonClapiException(sprintf('Could not find position %s', $position));
        }

        return $rows;
    }

    /**
     * Add resource to downtime
     *
     * @param $parameters
     * @param $object
     * @param $relTable
     * @param $relField
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    protected function addGenericRelation($parameters, $object, $relTable, $relField)
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // init var
        $downtimeId = $this->getObjectId($tmp[0]);
        $resources = explode('|', $tmp[1]);

        // retrieve object ids
        $objectIds = [];
        foreach ($resources as $resource) {
            $ids = $object->getIdByParameter($object->getUniqueLabelField(), [$resource]);

            // object does not exist
            if (! count($ids)) {
                throw new CentreonClapiException(sprintf('Unknown object named %s', $resource));
            }

            // checks whether or not relationship already exists
            $sql = "SELECT * FROM {$relTable} WHERE dt_id = ? AND {$relField} = ?";
            $stmt = $this->db->query($sql, [$downtimeId, $ids[0]]);
            if ($stmt->rowCount()) {
                throw new CentreonClapiException(sprintf('Relationship with %s already exists', $resource));
            }

            $objectIds[] = $ids[0];
        }

        // insert relationship
        $sql = "INSERT INTO {$relTable} (dt_id, {$relField}) VALUES (?, ?)";
        foreach ($objectIds as $id) {
            $this->db->query($sql, [$downtimeId, $id]);
        }
    }

    /**
     * Delete resource from downtime
     *
     * @param $parameters
     * @param $object
     * @param $relTable
     * @param $relField
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    protected function delGenericRelation($parameters, $object, $relTable, $relField)
    {
        $tmp = explode($this->delim, $parameters);
        if (count($tmp) != 2) {
            throw new CentreonClapiException('Missing parameters');
        }

        // init var
        $downtimeId = $this->getObjectId($tmp[0]);
        $resources = explode('|', $tmp[1]);

        // retrieve object ids
        $objectIds = [];
        foreach ($resources as $resource) {
            $ids = $object->getIdByParameter($object->getUniqueLabelField(), [$resource]);

            // object does not exist
            if (! count($ids)) {
                throw new CentreonClapiException(sprintf('Unknown object named %s', $resource));
            }

            // checks whether or not relationship already exists
            $sql = "SELECT * FROM {$relTable} WHERE dt_id = ? AND {$relField} = ?";
            $stmt = $this->db->query($sql, [$downtimeId, $ids[0]]);
            if (! $stmt->rowCount()) {
                throw new CentreonClapiException(
                    sprintf(
                        'Cannot remove relationship with %s as the relationship does not exist',
                        $resource
                    )
                );
            }

            $objectIds[] = $ids[0];
        }

        // delete relationship
        $sql = "DELETE FROM {$relTable} WHERE dt_id = ? AND {$relField} = ?";
        foreach ($objectIds as $id) {
            $this->db->query($sql, [$downtimeId, $id]);
        }
    }
}
