<?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/centreonUtils.class.php';
require_once _CENTREON_PATH_ . 'www/class/centreonCustomView.class.php';

use Adaptation\Database\Connection\Collection\QueryParameters;
use Adaptation\Database\Connection\Exception\ConnectionException;
use Adaptation\Database\Connection\ValueObject\QueryParameter;
use Core\Common\Domain\Exception\CollectionException;
use Core\Common\Domain\Exception\ValueObjectException;

/**
 * Class
 *
 * @class CentreonWidgetException
 */
class CentreonWidgetException extends Exception
{
}

/**
 * Class
 *
 * @class CentreonWidget
 */
class CentreonWidget
{
    /** @var CentreonCustomView */
    public $customView;

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

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

    /** @var array */
    protected $widgets = [];

    /** @var array */
    protected $userGroups = [];

    /**
     * CentreonWidget constructor
     *
     * @param Centreon $centreon
     * @param CentreonDB $db
     *
     * @throws Exception
     */
    public function __construct($centreon, $db)
    {
        $this->userId = (int) $centreon->user->user_id;
        $this->db = $db;
        $query = 'SELECT contactgroup_cg_id '
            . 'FROM contactgroup_contact_relation '
            . 'WHERE contact_contact_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $this->userId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        while ($row = $stmt->fetch()) {
            $this->userGroups[$row['contactgroup_cg_id']] = $row['contactgroup_cg_id'];
        }
        $this->customView = new CentreonCustomView($centreon, $db);
    }

    /**
     * @param int $widgetId
     * @throws Exception
     * @return null
     */
    public function getWidgetType($widgetId)
    {
        $query = 'SELECT widget_model_id, widget_id FROM widgets WHERE widget_id = :widgetId';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        while ($row = $stmt->fetch()) {
            return $row['widget_model_id'];
        }

        return null;
    }

    /**
     * @param int $widgetId
     * @throws Exception
     * @return null
     */
    public function getWidgetTitle($widgetId)
    {
        $query = 'SELECT title, widget_id FROM widgets WHERE widget_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $widgetId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        while ($row = $stmt->fetch()) {
            return htmlentities($row['title'], ENT_QUOTES);
        }

        return null;
    }

    /**
     * @param int $widgetModelId
     * @throws Exception
     * @return mixed
     */
    public function getWidgetDirectory($widgetModelId)
    {
        $query = 'SELECT directory FROM widget_models WHERE widget_model_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $widgetModelId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        while ($row = $stmt->fetch()) {
            return $row['directory'];
        }
    }

    /**
     * @param string|int $widgetModelId
     * @param string $name
     * @throws Exception
     * @return int
     */
    public function getParameterIdByName($widgetModelId, $name)
    {
        $tab = [];
        if (! isset($tab[$widgetModelId])) {
            $query = 'SELECT parameter_id, parameter_code_name '
                . 'FROM widget_parameters '
                . 'WHERE widget_model_id = :id';
            $tab[$widgetModelId] = [];
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':id', $widgetModelId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }

            while ($row = $stmt->fetch()) {
                $tab[$widgetModelId][$row['parameter_code_name']] = $row['parameter_id'];
            }
        }
        if (isset($tab[$widgetModelId][$name]) && $tab[$widgetModelId][$name]) {
            return $tab[$widgetModelId][$name];
        }

        return 0;
    }

    /**
     * @param string $type
     * @param string $param
     *
     * @throws PDOException
     * @return mixed|null
     */
    public function getWidgetInfo($type = 'id', $param = '')
    {
        static $tabDir;
        static $tabId;

        if (! isset($tabId) || ! isset($tabDir)) {
            $query = 'SELECT description, directory, title, widget_model_id, url, version, is_internal, author, '
                . 'email, website, keywords, screenshot, thumbnail, autoRefresh '
                . 'FROM widget_models';
            $res = $this->db->query($query);
            while ($row = $res->fetch()) {
                $widgetInformation = [
                    'description' => $row['description'],
                    'directory' => $row['directory'],
                    'title' => $row['title'],
                    'widget_model_id' => $row['widget_model_id'],
                    'url' => $row['url'],
                    'version' => $row['is_internal'] === 0 ? $row['version'] : null,
                    'is_internal' => $row['is_internal'] === 1,
                    'author' => $row['author'],
                    'email' => $row['email'],
                    'website' => $row['website'],
                    'keywords' => $row['keywords'],
                    'screenshot' => $row['screenshot'],
                    'thumbnail' => $row['thumbnail'],
                    'autoRefresh' => $row['autoRefresh'],
                ];
                $tabDir[$row['directory']] = $widgetInformation;
                $tabId[$row['widget_model_id']] = $widgetInformation;
            }
        }
        if ($type == 'directory' && isset($tabDir[$param])) {
            return $tabDir[$param];
        }
        if ($type == 'id' && isset($tabId[$param])) {
            return $tabId[$param];
        }

        return null;
    }

    /**
     * @param int $customViewId
     * @param int $widgetModelId
     * @param string $widgetTitle
     * @param bool $permission
     * @param bool $authorized
     *
     * @throws CentreonWidgetException
     * @throws Exception
     */
    public function addWidget(
        int $customViewId,
        int $widgetModelId,
        string $widgetTitle,
        bool $permission,
        bool $authorized,
    ): void {
        if (! $authorized || ! $permission) {
            throw new CentreonWidgetException('You are not allowed to add a widget.');
        }
        if (empty($customViewId) || empty($widgetModelId)) {
            throw new CentreonWidgetException('No custom view or no widget selected');
        }
        $query = 'INSERT INTO widgets (title, widget_model_id) VALUES (:title, :id)';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':title', $widgetTitle, PDO::PARAM_STR);
        $stmt->bindParam(':id', $widgetModelId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        // Get view layout
        $query = 'SELECT layout '
            . 'FROM custom_views '
            . 'WHERE custom_view_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $customViewId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new CentreonWidgetException('No view found');
        }
        $row = $stmt->fetch();
        if (is_null($row)) {
            throw new CentreonWidgetException('No view found');
        }
        $layout = str_replace('column_', '', $row['layout']);
        // Default position
        $newPosition = null;
        // Prepare first position
        $matrix = [];
        $query = 'SELECT widget_order '
            . 'FROM widget_views '
            . 'WHERE custom_view_id = :id';

        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $customViewId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new CentreonWidgetException('No view found');
        }

        while ($position = $stmt->fetch()) {
            [$col, $row] = explode('_', $position['widget_order']);
            if (isset($matrix[$row]) == false) {
                $matrix[$row] = [];
            }
            $matrix[$row][] = $col;
        }
        ksort($matrix);
        $rowNb = 0; // current row in the foreach
        foreach ($matrix as $row => $cols) {
            if ($rowNb != $row) {
                break;
            }
            if (count($cols) < $layout) {
                sort($cols); // number of used columns in this row
                for ($i = 0; $i < $layout; $i++) {
                    if (! isset($cols[$i]) || $cols[$i] != $i) {
                        $newPosition = $i . '_' . $rowNb;
                        break;
                    }
                }
                break;
            }
            $rowNb++;
        }
        if (is_null($newPosition)) {
            $newPosition = '0_' . $rowNb;
        }

        $lastId = $this->getLastInsertedWidgetId($widgetTitle);
        $query = 'INSERT INTO widget_views (custom_view_id, widget_id, widget_order) VALUES (:id, :lastId, :neworder)';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $customViewId, PDO::PARAM_INT);
        $stmt->bindParam(':lastId', $lastId, PDO::PARAM_INT);
        $stmt->bindParam(':neworder', $newPosition, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
    }

    /**
     * Get Wiget Info By Id
     *
     * @param int $widgetModelId
     *
     * @throws PDOException
     * @return mixed
     */
    public function getWidgetInfoById($widgetModelId)
    {
        return $this->getWidgetInfo('id', $widgetModelId);
    }

    /**
     * Get Widget Info By Directory
     *
     * @param string $directory
     *
     * @throws PDOException
     * @return mixed
     */
    public function getWidgetInfoByDirectory($directory)
    {
        return $this->getWidgetInfo('directory', $directory);
    }

    /**
     * @param string|int $widgetId
     * @throws CentreonWidgetException
     * @throws Exception
     * @return mixed
     */
    public function getUrl($widgetId)
    {
        $query = 'SELECT url FROM widget_models wm, widgets w '
            . 'WHERE wm.widget_model_id = w.widget_model_id '
            . 'AND w.widget_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $widgetId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        if ($stmt->rowCount()) {
            $row = $stmt->fetch();

            return $row['url'];
        }

        throw new CentreonWidgetException('No URL found for Widget #' . $widgetId);
    }

    /**
     * @param string|int $widgetId
     *
     * @throws CentreonWidgetException
     * @throws Exception
     * @return mixed
     */
    public function getRefreshInterval($widgetId)
    {
        $query = 'SELECT autoRefresh FROM widget_models wm, widgets w '
            . 'WHERE wm.widget_model_id = w.widget_model_id '
            . 'AND w.widget_id = :id';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':id', $widgetId, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        if ($stmt->rowCount()) {
            $row = $stmt->fetch();

            return $row['autoRefresh'];
        }

        throw new CentreonWidgetException('No autoRefresh found for Widget #' . $widgetId);
    }

    /**
     * @param int $viewId
     *
     * @throws Exception
     * @return array
     */
    public function getWidgetsFromViewId(int $viewId): array
    {
        if (! isset($this->widgets[$viewId])) {
            $this->widgets[$viewId] = [];
            $query = "SELECT w.widget_id, w.title, wm.url, widget_order
            		  FROM widget_views wv, widgets w, widget_models wm
            		  WHERE w.widget_id = wv.widget_id
            		  AND wv.custom_view_id = :id
            		  AND w.widget_model_id = wm.widget_model_id
                      ORDER BY 
                      CAST(SUBSTRING_INDEX(widget_order, '_', 1) AS SIGNED INTEGER), 
                      CAST(SUBSTRING_INDEX(widget_order, '_', -1) AS SIGNED INTEGER)";
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':id', $viewId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
            while ($row = $stmt->fetch()) {
                $this->widgets[$viewId][$row['widget_id']]['title'] = htmlentities($row['title'], ENT_QUOTES);
                $this->widgets[$viewId][$row['widget_id']]['url'] = $row['url'];
                $this->widgets[$viewId][$row['widget_id']]['widget_order'] = $row['widget_order'];
                $this->widgets[$viewId][$row['widget_id']]['widget_id'] = $row['widget_id'];
            }
        }

        return $this->widgets[$viewId];
    }

    /**
     * @param string $search
     * @param array $range
     *
     * @throws Exception
     * @return array
     */
    public function getWidgetModels($search = '', $range = [])
    {
        $queryValues = [];
        $query = 'SELECT SQL_CALC_FOUND_ROWS widget_model_id, title FROM widget_models ';
        if ($search != '') {
            $query .= 'WHERE title like :search ';
            $queryValues['search'] = '%' . $search . '%';
        }
        $query .= 'ORDER BY title ';
        if (! empty($range)) {
            $query .= 'LIMIT :offset, :limit ';
            $queryValues['offset'] = (int) $range[0];
            $queryValues['limit'] = (int) $range[1];
        }
        $stmt = $this->db->prepare($query);
        if (isset($queryValues['search'])) {
            $stmt->bindParam(':search', $queryValues['search'], PDO::PARAM_STR);
        }
        if (isset($queryValues['offset'])) {
            $stmt->bindParam(':offset', $queryValues['offset'], PDO::PARAM_INT);
            $stmt->bindParam(':limit', $queryValues['limit'], PDO::PARAM_INT);
        }
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        $widgets = [];
        while ($data = $stmt->fetch()) {
            $widgets[] = ['id' => $data['widget_model_id'], 'text' => $data['title']];
        }

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

    /**
     * @param int $viewId
     * @param array $widgetList
     *
     * @throws Exception
     */
    public function updateViewWidgetRelations($viewId, array $widgetList = []): void
    {
        $query = 'DELETE FROM widget_views WHERE custom_view_id = :viewId';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':viewId', $viewId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        $str = '';
        $queryValues = [];
        foreach ($widgetList as $widgetId) {
            if ($str != '') {
                $str .= ',';
            }
            $str .= '(:viewId, :widgetId' . $widgetId . ')';
            $queryValues['widgetId' . $widgetId] = (int) $widgetId;
        }

        if ($str != '') {
            $query = 'INSERT INTO widget_views (custom_view_id, widget_id) VALUES ' . $str;
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':viewId', $viewId, PDO::PARAM_INT);
            foreach ($queryValues as $widgetId) {
                $stmt->bindValue(':widgetId' . $widgetId, $widgetId, PDO::PARAM_INT);
            }
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        }
    }

    /**
     * @param string|int $widgetId
     * @param bool $hasPermission
     *
     * @throws Exception
     * @return array
     */
    public function getParamsFromWidgetId($widgetId, $hasPermission = false)
    {
        static $params;

        if (! isset($params)) {
            $params = [];
            $query = 'SELECT ft.is_connector, ft.ft_typename, p.parameter_id, p.parameter_name, p.default_value, '
                . 'p.header_title, p.require_permission '
                . 'FROM widget_parameters_field_type ft, widget_parameters p, widgets w '
                . 'WHERE ft.field_type_id = p.field_type_id '
                . 'AND p.widget_model_id = w.widget_model_id '
                . 'AND w.widget_id = :widgetId '
                . 'ORDER BY parameter_order ASC';
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }

            while ($row = $stmt->fetch()) {
                if ($row['require_permission'] && $hasPermission == false) {
                    continue;
                }
                $params[$row['parameter_id']]['parameter_id'] = $row['parameter_id'];
                $params[$row['parameter_id']]['ft_typename'] = $row['ft_typename'];
                $params[$row['parameter_id']]['parameter_name'] = $row['parameter_name'];
                $params[$row['parameter_id']]['default_value'] = $row['default_value'];
                $params[$row['parameter_id']]['is_connector'] = $row['is_connector'];
                $params[$row['parameter_id']]['header_title'] = $row['header_title'];
            }
        }

        return $params;
    }

    /**
     * @param array $params
     * @param bool $permission
     * @param bool $authorized
     *
     * @throws CentreonWidgetException
     * @throws PDOException
     */
    public function updateUserWidgetPreferences(array $params, bool $permission, bool $authorized): void
    {
        if (! $authorized || ! $permission) {
            throw new CentreonWidgetException('You are not allowed to set preferences on the widget');
        }
        $queryValues = [];
        $query = 'SELECT wv.widget_view_id '
            . 'FROM widget_views wv, custom_view_user_relation cvur '
            . 'WHERE cvur.custom_view_id = wv.custom_view_id '
            . 'AND wv.widget_id = :widgetId '
            . 'AND (cvur.user_id = :userId';
        $explodedValues = '';
        if (count($this->userGroups)) {
            foreach ($this->userGroups as $k => $v) {
                $explodedValues .= ':userGroupId' . $v . ',';
                $queryValues['userGroupId' . $v] = $v;
            }
            $explodedValues = rtrim($explodedValues, ',');

            $query .= " OR cvur.usergroup_id IN ({$explodedValues}) ";
        }
        $query .= ') AND wv.custom_view_id = :viewId';

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

        $stmt->bindParam(':widgetId', $params['widget_id'], PDO::PARAM_INT);
        $stmt->bindParam(':userId', $this->userId, PDO::PARAM_INT);
        $stmt->bindParam(':viewId', $params['custom_view_id'], PDO::PARAM_INT);
        if (count($this->userGroups)) {
            foreach ($queryValues as $key => $userGroupId) {
                $stmt->bindValue(':' . $key, $userGroupId, PDO::PARAM_INT);
            }
        }
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        if ($stmt->rowCount()) {
            $row = $stmt->fetch();
            $widgetViewId = $row['widget_view_id'];
        } else {
            throw new CentreonWidgetException('No widget_view_id found for user');
        }

        if ($permission == false) {
            $query = 'DELETE FROM widget_preferences '
                . 'WHERE widget_view_id = :widgetViewId '
                . 'AND user_id = :userId '
                . 'AND parameter_id NOT IN ('
                . 'SELECT parameter_id FROM widget_parameters WHERE require_permission = "1")';
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':widgetViewId', $widgetViewId, PDO::PARAM_INT);
            $stmt->bindParam(':userId', $this->userId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        } else {
            $query = 'DELETE FROM widget_preferences '
                . 'WHERE widget_view_id = :widgetViewId '
                . 'AND user_id = :userId';

            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':widgetViewId', $widgetViewId, PDO::PARAM_INT);
            $stmt->bindParam(':userId', $this->userId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        }

        $queryValues = [];
        $str = '';
        foreach ($params as $key => $val) {
            if (preg_match("/param_(\d+)/", $key, $matches)) {
                if (is_array($val)) {
                    if (isset($val['op_' . $matches[1]], $val['cmp_' . $matches[1]])) {
                        $val = $val['op_' . $matches[1]] . ' ' . $val['cmp_' . $matches[1]];
                    } elseif (isset($val['order_' . $matches[1]], $val['column_' . $matches[1]])) {
                        $val = $val['column_' . $matches[1]] . ' ' . $val['order_' . $matches[1]];
                    } elseif (isset($val['from_' . $matches[1]], $val['to_' . $matches[1]])) {
                        $val = $val['from_' . $matches[1]] . ',' . $val['to_' . $matches[1]];
                    } else {
                        $val = implode(',', $val);
                    }
                    $val = trim($val);
                }
                if ($str != '') {
                    $str .= ', ';
                }
                $str .= '(:widgetViewId, :parameter' . $matches[1] . ', :preference' . $matches[1] . ', :userId)';
                $queryValues['parameter'][':parameter' . $matches[1]] = $matches[1];
                $queryValues['preference'][':preference' . $matches[1]] = $val;
            }
        }
        if ($str != '') {
            $query = 'INSERT INTO widget_preferences (widget_view_id, parameter_id, preference_value, user_id) '
                . 'VALUES ' . $str;
            $stmt = $this->db->prepare($query);
            $stmt->bindValue(':widgetViewId', $widgetViewId, PDO::PARAM_INT);
            $stmt->bindValue(':userId', $this->userId, PDO::PARAM_INT);
            if (isset($queryValues['parameter'])) {
                foreach ($queryValues['parameter'] as $k => $v) {
                    $stmt->bindValue($k, $v, PDO::PARAM_INT);
                }
            }
            if (isset($queryValues['preference'])) {
                foreach ($queryValues['preference'] as $k => $v) {
                    $stmt->bindValue($k, $v, PDO::PARAM_STR);
                }
            }
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        }
        $this->customView->syncCustomView($params['custom_view_id']);
    }

    /**
     * @param int $customViewId
     * @param int $widgetId
     * @param bool $authorized
     * @param bool $permission
     *
     * @throws Exception
     */
    public function deleteWidgetFromView(int $customViewId, int $widgetId, bool $authorized, bool $permission): void
    {
        if (! $authorized || ! $permission) {
            throw new CentreonWidgetException('You are not allowed to delete the widget');
        }
        $query = 'DELETE FROM widget_views '
            . 'WHERE custom_view_id = :viewId '
            . 'AND widget_id = :widgetId';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':viewId', $customViewId, PDO::PARAM_INT);
        $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
    }

    /**
     * Updates a widget position on a customview
     *
     * @param int $customViewId
     * @param bool $permission
     * @param array $positions
     *
     * @throws CentreonWidgetException
     * @throws PDOException
     */
    public function updateWidgetPositions(int $customViewId, bool $permission, array $positions = []): void
    {
        if (! $permission) {
            throw new CentreonWidgetException('You are not allowed to change widget position');
        }
        if (empty($customViewId)) {
            throw new CentreonWidgetException('No custom view id provided');
        }
        if ($positions !== [] && is_array($positions)) {
            foreach ($positions as $rawData) {
                if (preg_match('/([0-9]+)_([0-9]+)_([0-9]+)/', $rawData, $matches)) {
                    $widgetOrder = "{$matches[1]}_{$matches[2]}";
                    $widgetId = $matches[3];

                    $query = 'UPDATE widget_views SET widget_order = :widgetOrder '
                        . 'WHERE custom_view_id = :viewId '
                        . 'AND widget_id = :widgetId';
                    $stmt = $this->db->prepare($query);
                    $stmt->bindParam(':widgetOrder', $widgetOrder, PDO::PARAM_STR);
                    $stmt->bindParam(':viewId', $customViewId, PDO::PARAM_INT);
                    $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
                    $dbResult = $stmt->execute();

                }
            }
        }
    }

    /**
     * Read Configuration File
     *
     * @param string $filename
     *
     * @return array
     */
    public function readConfigFile($filename)
    {
        $xmlString = file_get_contents($filename);
        $xmlObj = simplexml_load_string($xmlString);

        return (new CentreonUtils())->objectIntoArray($xmlObj);
    }

    /**
     * @param string $widgetPath
     * @param string $directory
     *
     * @throws Exception
     */
    public function install($widgetPath, $directory): void
    {
        $config = $this->readConfigFile($widgetPath . '/' . $directory . '/configs.xml');
        if (! $config['autoRefresh']) {
            $config['autoRefresh'] = 0;
        }
        $queryValues = [];
        $query = 'INSERT INTO widget_models (title, description, url, version, directory, author, email, '
            . 'website, keywords, screenshot, thumbnail, autoRefresh) '
            . 'VALUES (:title, :description, :url, :version, :directory, :author, :email, '
            . ':website, :keywords, :screenshot, :thumbnail, :autoRefresh)';

        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':title', $config['title'], PDO::PARAM_STR);
        $stmt->bindParam(':description', $config['description'], PDO::PARAM_STR);
        $stmt->bindParam(':url', $config['url'], PDO::PARAM_STR);
        $stmt->bindParam(':version', $config['version'], PDO::PARAM_STR);
        $stmt->bindParam(':directory', $directory, PDO::PARAM_STR);
        $stmt->bindParam(':author', $config['author'], PDO::PARAM_STR);
        $stmt->bindParam(':email', $config['email'], PDO::PARAM_STR);
        $stmt->bindParam(':website', $config['website'], PDO::PARAM_STR);
        $stmt->bindParam(':keywords', $config['keywords'], PDO::PARAM_STR);
        $stmt->bindParam(':screenshot', $config['screenshot'], PDO::PARAM_STR);
        $stmt->bindParam(':thumbnail', $config['thumbnail'], PDO::PARAM_STR);
        $stmt->bindParam(':autoRefresh', $config['autoRefresh'], PDO::PARAM_INT);

        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        $lastId = $this->getLastInsertedWidgetModelId($directory);
        $this->insertWidgetPreferences($lastId, $config);
    }

    /**
     * @param string $widgetPath
     * @param string $directory
     *
     * @throws Exception
     */
    public function upgrade($widgetPath, $directory): void
    {
        $config = $this->readConfigFile($widgetPath . '/' . $directory . '/configs.xml');
        if (! $config['autoRefresh']) {
            $config['autoRefresh'] = 0;
        }
        $queryValues = [];
        $query = 'UPDATE widget_models SET '
            . 'title = ?, '
            . 'description = ?, '
            . 'url = ?, '
            . 'version = ?, '
            . 'author = ?, '
            . 'email = ?, '
            . 'website = ?, '
            . 'keywords = ?, '
            . 'screenshot = ?, '
            . 'thumbnail = ?, '
            . 'autoRefresh = ? '
            . 'WHERE directory = ?';

        $queryValues[] = (string) $config['title'];
        $queryValues[] = (string) $config['description'];
        $queryValues[] = (string) $config['url'];
        $queryValues[] = (string) $config['version'];
        $queryValues[] = (string) $config['author'];
        $queryValues[] = (string) $config['email'];
        $queryValues[] = (string) $config['website'];
        $queryValues[] = (string) $config['keywords'];
        $queryValues[] = (string) $config['screenshot'];
        $queryValues[] = (string) $config['thumbnail'];
        $queryValues[] = (int) $config['autoRefresh'];
        $queryValues[] = (string) $directory;

        $stmt = $this->db->prepare($query);
        $dbResult = $stmt->execute($queryValues);
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        $info = $this->getWidgetInfoByDirectory($directory);
        $this->upgradePreferences($info['widget_model_id'], $config);
    }

    /**
     * @param string $directory
     *
     * @throws Exception
     */
    public function uninstall($directory): void
    {
        $query = 'DELETE FROM widget_models WHERE directory = :directory';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':directory', $directory, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
    }

    /**
     * @param int $widgetId
     *
     * @throws Exception
     * @return array
     */
    public function getWidgetPreferences($widgetId)
    {
        $query = 'SELECT default_value, parameter_code_name '
            . 'FROM widget_parameters param, widgets w '
            . 'WHERE w.widget_model_id = param.widget_model_id '
            . 'AND w.widget_id = :widgetId';

        // Prevent SQL injection with widget id
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }

        $tab = [];
        while ($row = $stmt->fetch()) {
            $tab[$row['parameter_code_name']] = $row['default_value'];
        }

        try {
            $query = 'SELECT pref.preference_value, param.parameter_code_name '
                . 'FROM widget_preferences pref, widget_parameters param, widget_views wv '
                . 'WHERE param.parameter_id = pref.parameter_id '
                . 'AND pref.widget_view_id = wv.widget_view_id '
                . 'AND wv.widget_id = :widgetId '
                . 'AND pref.user_id = :userId';

            // Prevent SQL injection with widget id
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
            $stmt->bindParam(':userId', $this->userId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
        } catch (PDOException $e) {
            throw new Exception(
                'Error: cannot get preference parameter by user for widget , ' . $e->getMessage() . "\n"
            );
        }

        // if user has no preferences take parent preferences
        if ($this->db->numberRows() === 0) {
            try {
                $query = 'SELECT pref.preference_value, param.parameter_code_name '
                    . 'FROM widget_preferences pref, widget_parameters param, widget_views wv '
                    . 'WHERE param.parameter_id = pref.parameter_id '
                    . 'AND pref.widget_view_id = wv.widget_view_id '
                    . 'AND wv.widget_id = :widgetId ';
                // Prevent SQL injection with widget id
                $stmt = $this->db->prepare($query);
                $stmt->bindParam(':widgetId', $widgetId, PDO::PARAM_INT);
                $dbResult = $stmt->execute();
            } catch (PDOException $e) {
                throw new Exception(
                    'Error: cannot get preference parameter for widget , ' . $e->getMessage() . "\n"
                );
            }
        }

        while ($row = $stmt->fetch()) {
            $tab[$row['parameter_code_name']] = $row['preference_value'];
        }

        return $tab;
    }

    /**
     * Rename widget
     *
     * @param int $widgetId
     * @param string $newName widget new name
     *
     * @throws PDOException
     * @return string
     */
    public function rename(int $widgetId, string $newName): string
    {
        global $centreon;
        // ACL check
        if ($centreon->user->admin === '1') {
            $updateSql = <<<'SQL'
                    UPDATE widgets
                    SET title = :title
                    WHERE widget_id = :widgetId
                SQL;
        } else {
            $goupIds = $this->userGroups !== [] ? implode(',', $this->userGroups) : '';
            $groupClause = $goupIds ? " OR cvur.usergroup_id IN ({$goupIds})" : '';
            $updateSql = <<<'SQL'
                    UPDATE widgets AS w
                    INNER JOIN widget_views AS wv
                        ON wv.widget_id = w.widget_id
                    SET w.title = :title
                    WHERE w.widget_id = :widgetId
                    AND (
                            /* Case 1: user is owner of the parent custom view */
                            EXISTS (
                                SELECT 1
                                FROM custom_view_user_relation AS cvur
                                WHERE cvur.custom_view_id = wv.custom_view_id
                                AND cvur.user_id = :userId
                                AND cvur.is_owner = 1
                            )
                            OR
                            /* Case 2: (user OR any group) AND not locked */
                            EXISTS (
                                SELECT 1
                                FROM custom_view_user_relation AS cvur
                                WHERE cvur.custom_view_id = wv.custom_view_id
                                AND cvur.locked = 0
                                AND (
                                        cvur.user_id = :userId
                                        {$groupClause}
                                    )
                            )
                        )
                SQL;
        }
        try {
            $params = [
                QueryParameter::int('widgetId', $widgetId),
                QueryParameter::string('title', $newName),
            ];
            if ($centreon->user->admin !== '1') {
                $params[] = QueryParameter::int('userId', $this->userId);
            }
            $queryParameters = QueryParameters::create($params);
            $nbAffectedRows = $this->db->update($updateSql, $queryParameters);

            // If no row → no permission
            if ($nbAffectedRows === 0) {
                $msgError = 'Access denied to rename widget';
                CentreonLog::create()->error(
                    CentreonLog::TYPE_BUSINESS_LOG,
                    $msgError,
                    [
                        'widgetId' => $widgetId,
                        'userId' => $this->userId,
                        'newTitle' => $newName,
                    ],
                );

                return $msgError;
            }

            return $newName;
        } catch (ValueObjectException|CollectionException|ConnectionException $exception) {
            $msgError = 'Error while renaming widget: ' . $exception->getMessage();
            CentreonLog::create()->error(
                CentreonLog::TYPE_SQL,
                $msgError,
                [
                    'widgetId' => $widgetId,
                    'userId' => $this->userId,
                    'newTitle' => $newName,
                ],
                $exception
            );

            return $msgError;
        }
    }

    /**
     * @param int $widgetModelId
     * @throws Exception
     * @return array
     */
    protected function getParamsFromWidgetModelId($widgetModelId)
    {
        static $tab;

        if (! isset($tab)) {
            $query = 'SELECT parameter_code_name '
                . 'FROM widget_parameters '
                . 'WHERE widget_model_id = :modelId';
            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':modelId', $widgetModelId, PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
            $tab = [];
            while ($row = $stmt->fetch()) {
                $tab[$row['parameter_code_name']] = $row['parameter_code_name'];
            }
        }

        return $tab;
    }

    /**
     * @param string $title
     *
     * @throws Exception
     * @return mixed
     */
    protected function getLastInsertedWidgetId($title)
    {
        $query = 'SELECT MAX(widget_id) as lastId FROM widgets WHERE title = :title';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':title', $title, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        $row = $stmt->fetch();

        return $row['lastId'];
    }

    /**
     * @param string $directory
     *
     * @throws PDOException
     * @return mixed
     */
    protected function getLastInsertedWidgetModelId($directory)
    {
        $query = 'SELECT MAX(widget_model_id) as lastId '
            . 'FROM widget_models '
            . 'WHERE directory = :directory';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':directory', $directory, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        $row = $stmt->fetch();

        return $row['lastId'];
    }

    /**
     * @param string $label
     *
     * @throws Exception
     * @return mixed
     */
    protected function getLastInsertedParameterId($label)
    {
        $query = 'SELECT MAX(parameter_id) as lastId '
            . 'FROM widget_parameters '
            . 'WHERE parameter_name = :name';
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':name', $label, PDO::PARAM_STR);
        $dbResult = $stmt->execute();
        if (! $dbResult) {
            throw new Exception('An error occured');
        }
        $row = $stmt->fetch();

        return $row['lastId'];
    }

    /**
     * Get Parameter Type IDs
     *
     * @throws PDOException
     * @return array
     */
    protected function getParameterTypeIds()
    {
        static $types;

        if (! isset($types)) {
            $types = [];
            $query = 'SELECT ft_typename, field_type_id FROM  widget_parameters_field_type';
            $res = $this->db->query($query);
            while ($row = $res->fetch()) {
                $types[$row['ft_typename']] = $row['field_type_id'];
            }
        }

        return $types;
    }

    /**
     * @param int $lastId
     * @param array $config
     *
     * @throws CentreonWidgetException
     * @throws PDOException
     * @throws Exception
     */
    protected function insertWidgetPreferences($lastId, $config)
    {
        if (isset($config['preferences'])) {
            $types = $this->getParameterTypeIds();
            foreach ($config['preferences'] as $preference) {
                $order = 1;
                if (isset($preference['@attributes'])) {
                    $pref = $preference;
                    $attr = $pref['@attributes'];
                    if (! isset($types[$attr['type']])) {
                        throw new CentreonWidgetException(
                            'Unknown type : ' . $attr['type'] . ' found in configuration file'
                        );
                    }
                    if (! isset($attr['requirePermission'])) {
                        $attr['requirePermission'] = 0;
                    }
                    if (! isset($attr['defaultValue'])) {
                        $attr['defaultValue'] = '';
                    }
                    $str = '(:lastId, :type, :label, :name, :defaultValue, :order, :requirePermission, ';
                    if (isset($attr['header']) && $attr['header'] != '') {
                        $str .= ':header';
                    } else {
                        $str .= 'NULL';
                    }
                    $str .= ')';
                    $query = 'INSERT INTO widget_parameters '
                        . '(widget_model_id, field_type_id, parameter_name, parameter_code_name, default_value, '
                        . 'parameter_order, require_permission, header_title) '
                        . 'VALUES ' . $str;
                    $stmt = $this->db->prepare($query);
                    $stmt->bindParam(':lastId', $lastId, PDO::PARAM_INT);
                    $stmt->bindParam(':type', $types[$attr['type']], PDO::PARAM_INT);
                    $stmt->bindParam(':label', $attr['label'], PDO::PARAM_STR);
                    $stmt->bindParam(':name', $attr['name'], PDO::PARAM_STR);
                    $stmt->bindParam(':defaultValue', $attr['defaultValue'], PDO::PARAM_STR);
                    $stmt->bindParam(':order', $order, PDO::PARAM_INT);
                    $stmt->bindParam(':requirePermission', $attr['requirePermission'], PDO::PARAM_STR);
                    if (isset($attr['header']) && $attr['header'] != '') {
                        $stmt->bindParam(':header', $attr['header'], PDO::PARAM_STR);
                    }
                    $dbResult = $stmt->execute();
                    if (! $dbResult) {
                        throw new Exception('An error occured');
                    }
                    $lastParamId = $this->getLastInsertedParameterId($attr['label']);
                    $this->insertParameterOptions($lastParamId, $attr, $pref);
                    $order++;
                } else {
                    foreach ($preference as $pref) {
                        $attr = $pref['@attributes'];
                        if (! isset($types[$attr['type']])) {
                            throw new CentreonWidgetException(
                                'Unknown type : ' . $attr['type'] . ' found in configuration file'
                            );
                        }
                        if (! isset($attr['requirePermission'])) {
                            $attr['requirePermission'] = 0;
                        }
                        if (! isset($attr['defaultValue'])) {
                            $attr['defaultValue'] = '';
                        }

                        $str = '(:lastId, :type, :label, :name, :defaultValue, :order, :requirePermission, ';
                        if (isset($attr['header']) && $attr['header'] != '') {
                            $str .= ':header';
                        } else {
                            $str .= 'NULL';
                        }
                        $str .= ')';
                        $query = 'INSERT INTO widget_parameters '
                            . '(widget_model_id, field_type_id, parameter_name, parameter_code_name, '
                            . 'default_value, parameter_order, require_permission, header_title) '
                            . 'VALUES ' . $str;
                        $stmt = $this->db->prepare($query);
                        $stmt->bindParam(':lastId', $lastId, PDO::PARAM_INT);
                        $stmt->bindParam(':type', $types[$attr['type']], PDO::PARAM_INT);
                        $stmt->bindParam(':label', $attr['label'], PDO::PARAM_STR);
                        $stmt->bindParam(':name', $attr['name'], PDO::PARAM_STR);
                        $stmt->bindParam(':defaultValue', $attr['defaultValue'], PDO::PARAM_STR);
                        $stmt->bindParam(':order', $order, PDO::PARAM_INT);
                        $stmt->bindParam(':requirePermission', $attr['requirePermission'], PDO::PARAM_STR);
                        if (isset($attr['header']) && $attr['header'] != '') {
                            $stmt->bindParam(':header', $attr['header'], PDO::PARAM_STR);
                        }
                        $dbResult = $stmt->execute();
                        if (! $dbResult) {
                            throw new Exception('An error occured');
                        }

                        $lastParamId = $this->getLastInsertedParameterId($attr['label']);
                        $this->insertParameterOptions($lastParamId, $attr, $pref);
                        $order++;
                    }
                }
            }
        }
    }

    /**
     * @param int $paramId
     * @param array $attr
     * @param array $pref
     *
     * @throws Exception
     */
    protected function insertParameterOptions($paramId, $attr, $pref)
    {
        if ($attr['type'] == 'list' || $attr['type'] == 'sort') {
            if (isset($pref['option'])) {
                $queryValues2 = [];
                $str2 = '';
                foreach ($pref['option'] as $option) {
                    $opt = $option['@attributes'] ?? $option;
                    if ($str2 != '') {
                        $str2 .= ', ';
                    }
                    $str2 .= '(:id, :label, :value)';
                }
                if ($str2 != '') {
                    $query2 = 'INSERT INTO widget_parameters_multiple_options '
                        . '(parameter_id, option_name, option_value) '
                        . 'VALUES ' . $str2;
                    $stmt = $this->db->prepare($query2);
                    $stmt->bindParam(':id', $paramId, PDO::PARAM_INT);
                    $stmt->bindParam(':label', $opt['label'], PDO::PARAM_STR);
                    $stmt->bindParam(':value', $opt['value'], PDO::PARAM_STR);
                    $dbResult = $stmt->execute();
                    if (! $dbResult) {
                        throw new Exception('An error occured');
                    }
                }
            }
        } elseif ($attr['type'] == 'range') {
            $query = 'INSERT INTO widget_parameters_range (parameter_id, min_range, max_range, step) '
                . 'VALUES (:id, :mini, :maxi, :step)';

            $stmt = $this->db->prepare($query);
            $stmt->bindParam(':id', $paramId, PDO::PARAM_INT);
            $stmt->bindParam(':mini', $attr['min'], PDO::PARAM_INT);
            $stmt->bindParam(':maxi', $attr['max'], PDO::PARAM_INT);
            $stmt->bindParam(':step', $attr['step'], PDO::PARAM_INT);
            $dbResult = $stmt->execute();
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        }
    }

    /**
     * @param int $widgetModelId
     * @param array $config
     *
     * @throws CentreonWidgetException
     * @throws Exception
     */
    protected function upgradePreferences($widgetModelId, $config)
    {
        $existingParams = $this->getParamsFromWidgetModelId($widgetModelId);
        $currentParameterTab = [];
        if (isset($config['preferences'])) {
            $types = $this->getParameterTypeIds();
            foreach ($config['preferences'] as $preference) {
                $order = 1;
                if (isset($preference['@attributes'])) {
                    $pref = $preference;
                    $attr = $pref['@attributes'];
                    if (! isset($types[$attr['type']])) {
                        throw new CentreonWidgetException(
                            'Unknown type : ' . $attr['type'] . ' found in configuration file'
                        );
                    }
                    if (! isset($existingParams[$attr['name']])) {
                        if (! isset($attr['requirePermission'])) {
                            $attr['requirePermission'] = 0;
                        }
                        $queryValues = [];
                        $str = '(?, ?, ?, ?, ?, ?, ?, ';
                        $queryValues[] = (int) $widgetModelId;
                        $queryValues[] = (int) $types[$attr['type']];
                        $queryValues[] = (string) $attr['label'];
                        $queryValues[] = (string) $attr['name'];
                        $queryValues[] = (string) $attr['defaultValue'];
                        $queryValues[] = (int) $order;
                        $queryValues[] = (string) $attr['requirePermission'];
                        if (isset($attr['header']) && $attr['header'] != '') {
                            $str .= '?';
                            $queryValues[] = (string) $attr['header'];
                        } else {
                            $str .= 'NULL';
                        }
                        $str .= ')';
                        $query = 'INSERT INTO widget_parameters (widget_model_id, field_type_id, parameter_name, '
                            . 'parameter_code_name, default_value, parameter_order, require_permission, header_title) '
                            . 'VALUES ' . $str;
                    } else {
                        $queryValues = [];
                        $str = ' field_type_id = ?, parameter_name = ?,  default_value = ?, parameter_order = ?, ';
                        $queryValues[] = (int) $types[$attr['type']];
                        $queryValues[] = (string) $attr['label'];
                        $queryValues[] = (string) $attr['defaultValue'];
                        $queryValues[] = (int) $order;

                        if (! isset($attr['requirePermission'])) {
                            $attr['requirePermission'] = 0;
                        }
                        $str .= ' require_permission = ?, header_title = ';
                        $queryValues[] = (string) $attr['requirePermission'];

                        if (isset($attr['header']) && $attr['header'] != '') {
                            $str .= '? ';
                            $queryValues[] = (string) $attr['header'];
                        } else {
                            $str .= 'NULL ';
                        }
                        $query = 'UPDATE widget_parameters '
                            . 'SET ' . $str . ' '
                            . 'WHERE parameter_code_name = ? '
                            . 'AND widget_model_id = ?';
                        $queryValues[] = (string) $attr['name'];
                        $queryValues[] = (int) $widgetModelId;
                    }
                    $stmt = $this->db->prepare($query);
                    $dbResult = $stmt->execute($queryValues);
                    if (! $dbResult) {
                        throw new Exception('An error occured');
                    }

                    $parameterId = $this->getParameterIdByName($widgetModelId, $attr['name']);
                    $currentParameterTab[$attr['name']] = 1;
                    $query = 'DELETE FROM widget_parameters_multiple_options WHERE parameter_id = :paramId';
                    $stmt = $this->db->prepare($query);
                    $stmt->bindParam(':paramId', $parameterId, PDO::PARAM_INT);
                    $dbResult = $stmt->execute();
                    if (! $dbResult) {
                        throw new Exception('An error occured');
                    }

                    $this->insertParameterOptions($parameterId, $attr, $pref);
                    $order++;
                } else {
                    foreach ($preference as $pref) {
                        $attr = $pref['@attributes'];
                        if (! isset($types[$attr['type']])) {
                            throw new CentreonWidgetException(
                                'Unknown type : ' . $attr['type'] . ' found in configuration file'
                            );
                        }
                        if (! isset($existingParams[$attr['name']])) {
                            if (! isset($attr['requirePermission'])) {
                                $attr['requirePermission'] = 0;
                            }

                            $queryValues = [];
                            $str = '(?, ?, ?, ?, ?, ?, ?, ';
                            $queryValues[] = (int) $widgetModelId;
                            $queryValues[] = (int) $types[$attr['type']];
                            $queryValues[] = (string) $attr['label'];
                            $queryValues[] = (string) $attr['name'];
                            $queryValues[] = (string) $attr['defaultValue'];
                            $queryValues[] = (int) $order;
                            $queryValues[] = (string) $attr['requirePermission'];

                            if (! isset($attr['header'])) {
                                $str .= 'NULL';
                            } else {
                                $str .= '?';
                                $queryValues[] = (string) $attr['header'];
                            }
                            $str .= ')';
                            $query = 'INSERT INTO widget_parameters (widget_model_id, field_type_id, parameter_name, '
                                . 'parameter_code_name, default_value, parameter_order, require_permission, '
                                . 'header_title) VALUES ' . $str;
                        } else {
                            $queryValues = [];
                            $str = ' field_type_id = ?, parameter_name = ?, ';
                            $queryValues[] = (int) $types[$attr['type']];
                            $queryValues[] = (string) $attr['label'];

                            if (isset($attr['defaultValue'])) {
                                $str .= ' default_value = ?, ';
                                $queryValues[] = (string) $attr['defaultValue'];
                            }
                            $str .= ' parameter_order = ?, ';
                            $queryValues[] = (int) $order;

                            if (! isset($attr['requirePermission'])) {
                                $attr['requirePermission'] = 0;
                            }
                            $str .= ' require_permission = ?, header_title = ';
                            $queryValues[] = (string) $attr['requirePermission'];

                            if (isset($attr['header']) && $attr['header'] != '') {
                                $str .= '? ';
                                $queryValues[] = (string) $attr['header'];
                            } else {
                                $str .= 'NULL ';
                            }
                            $query = 'UPDATE widget_parameters SET ' . $str . ' '
                                . 'WHERE parameter_code_name = ? '
                                . 'AND widget_model_id = ?';
                            $queryValues[] = (string) $attr['name'];
                            $queryValues[] = (int) $widgetModelId;
                        }
                        $stmt = $this->db->prepare($query);
                        $dbResult = $stmt->execute($queryValues);
                        if (! $dbResult) {
                            throw new Exception('An error occured');
                        }

                        $parameterId = $this->getParameterIdByName($widgetModelId, $attr['name']);
                        $currentParameterTab[$attr['name']] = 1;
                        $query = 'DELETE FROM widget_parameters_multiple_options WHERE parameter_id = :paramId';
                        $stmt = $this->db->prepare($query);
                        $stmt->bindParam(':paramId', $parameterId, PDO::PARAM_INT);
                        $dbResult = $stmt->execute();
                        if (! $dbResult) {
                            throw new Exception('An error occured');
                        }

                        $this->insertParameterOptions($parameterId, $attr, $pref);
                        $order++;
                    }
                }
            }
        }
        $deleteStr = '';
        $deleteQueryValues = [];
        foreach ($existingParams as $codeName) {
            if (! isset($currentParameterTab[$codeName])) {
                if ($deleteStr != '') {
                    $deleteStr .= ', ';
                }
                $deleteStr .= '?';
                $codeName = array_map(
                    function ($var) {
                        return (string) $var;
                    },
                    $codeName
                );
                $deleteQueryValues[] = $codeName;
            }
        }
        if ($deleteStr) {
            $query = 'DELETE FROM widget_parameters '
                . 'WHERE parameter_code_name '
                . 'IN (' . $deleteStr . ') '
                . 'AND widget_model_id = ?';
            $deleteQueryValues[] = (int) $widgetModelId;
            $stmt = $this->db->prepare($query);
            $dbResult = $stmt->execute($deleteQueryValues);
            if (! $dbResult) {
                throw new Exception('An error occured');
            }
        }
    }
}
