<?php
require_once APPPATH . 'modules/dashboard/models/entities/CF_Dashboard.php';
require_once APPPATH . 'modules/dashboard/models/entities/CF_Alert.php';

class dashboard_model extends Cf_Model
{
    public $collectionName = 'dashboard_dashboards';
    public $userCollectionName = 'users';
    private $widgetsCollectionName = 'dashboard_widgets';
    private $alertsCollectionName = 'dashboard_alerts';

    public $defaultFilter;

    public function __construct()
    {
        parent::__construct();
    }

    public function setDefaultFilter($filter)
    {
        $this->defaultFilter = $filter;
    }

    /*
     * Get all dashboards
     */
    public function get_all_dashboards($filter = [])
    {
        $query = $this->db->where($filter)->get($this->collectionName);
        $result = [];

        foreach ((array) $query->result_array() as $dashResult) {
            $obj = new CF_Dashboard($dashResult);
            $result[] = $obj;
        }

        return $result;
    }

    /*
     * Get all public dashboards - filter is 'public' boolean == true
     */
    public function get_public_dashboards($filter = [])
    {
        $query = $this->db->where($filter)->get($this->collectionName);
        $result = [];

        foreach ((array) $query->result_array() as $dashResult) {
            $obj = new CF_Dashboard($dashResult);
            $result[] = $obj;
        }

        return $result;
    }

    /*
     * Get all users's dashboards - filter is username only
     */
    public function get_user_allowed_dashboards($filter = [])
    {
        $query = $this->db
            ->group_start()
            ->where('public = 1')
            ->group_start()
            ->where("sharedwith->'sharedWithAll' = 'true'", null, false)
            ->or_where("sharedwith->'users' @> '\"{$filter['username']}\"'", null, false);

        if (isset($filter['roles']) && !empty($filter['roles'])) {
            foreach ($filter['roles'] as $role) {
                $query->or_where("sharedwith->'roles' @> '\"$role\"'", null, false);
            }
        }

        $query->group_end()->group_end()->or_where('username', $filter['username']);

        if (isset($filter['id'])) {
            $query->where('id', $filter['id']);
        }

        $query = $query->get($this->collectionName);
        $result = [];

        foreach ((array) $query->result_array() as $dashResult) {
            $obj = new CF_Dashboard($dashResult);
            $result[] = $obj;
        }

        return $result;
    }

    public function get_user_dashboards($filter = [])
    {
        if (isset($filter['username'])) {
            $query = $this->db->where('username', $filter['username'])->get($this->collectionName);
        }

        $result = [];

        foreach ((array) $query->result_array() as $dashResult) {
            $obj = new CF_Dashboard($dashResult);
            $result[] = $obj;
        }

        return $result;
    }

    public function reset_user_selection($filter = '')
    {
        if (!is_array($filter)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        $data = ['dashboard' => null];
        $this->db->where($filter);

        try {
            $this->db->update($this->userCollectionName, $data);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }

        return;
    }

    public function remove_dashboard($filter = '')
    {
        if (!is_array($filter)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        try {
            $return = $this->db->where($filter)->delete($this->collectionName);
            return $return;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    public function save_dashboard($data, $overwrite)
    {
        if (!is_array($data)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        $id = '';

        $filter['username'] = $data['username'];

        if (isset($data['id'])) {
            $filter['id'] = intval($data['id']);
            unset($data['id']);
        } else {
            $filter['name'] = $data['name'];
        }

        if ($this->validate($data, $overwrite) === false) {
            $message = $this->getErrors();
            $text = $this->lang->line('dashboard_unable_to_save') . ' ' . $message[0]['text'];
            throw new Exception($text);
        }

        $currentItem = $this->get_all_dashboards($filter);

        if (!empty($currentItem)) {
            $id = $currentItem[0]->id;
            $owner = $currentItem[0]->getUsername();

            // $data['username'] - user who created wizard, in most cases current logged user
            if ($owner !== $data['username']) {
                $data['username'] = $owner;
            }
        }

        if (isset($data['sharedWith'])) {
            $data['sharedWith'] = json_encode($data['sharedWith']);
        }

        if ($id == '') {
            // not exist - insert it will return inserted item
            $this->db->insert($this->collectionName, $data);
            $id = $this->db->insert_id();
        } else {
            // if exist - update, and then select inserted item
            // TURNS ARRAY INTO COMMA STRING , MIGHT NEED TO FIX when real DB
            if (is_array($data['widgets'])) {
                $comma_separated = implode(',', $data['widgets']);
                $data['widgets'] = $comma_separated;
            }
            $this->db->where(['id' => $id])->update($this->collectionName, $data);
        }

        // get object from DB
        $offset = '';
        $limit = '';
        $order = [];

        $objTmp = $this->get_all_dashboards(['id' => $id]);

        if ($objTmp) {
            $dashboard = $objTmp[0];
            return $dashboard;
        } else {
            // shouldn't happen
            throw new Exception($this->lang->line('dashboard_widget_unable_to_load'));
        }
    }

    /*
     * Reorder widgets and updates dahboard where ID and USERNAME match current user and selected ID
     */
    public function reorder_widgets($filter = [], $data = [])
    {
        if (!is_array($filter)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }
        if (!is_array($data)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        // if exist - update, and then select inserted item
        // TURNS ARRAY INTO COMMA STRING , MIGHT NEED TO FIX when real DB
        $comma_separated = implode(',', $data['widgets']);
        $data = $comma_separated;

        $this->db->where($filter)->update($this->collectionName, ['widgets' => $data]);
        $objTmp = $this->get_all_dashboards($filter);

        if ($objTmp) {
            $dashboard = $objTmp[0];
            return $dashboard;
        } else {
            // shouldn't happen
            throw new Exception($this->lang->line('dashboard_widget_unable_to_load'));
        }
    }

    /*
     * save selected dashboard to user's data
     */
    public function set_user_selection($filter = [])
    {
        if (!is_array($filter)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        $this->db->where(['username' => $filter['username']]);
        $query = $this->db->get($this->userCollectionName);
        $userObj = $query->result_array();

        if ($userObj) {
            $userObj[0]['dashboard'] = $filter['id'] ?? null;
            $this->db->where(['username' => $filter['username']])->update($this->userCollectionName, $userObj[0]);
            return $userObj[0];
        }

        return;
    }

    /*
     * get selected dashboard for user
     */
    public function get_user_selection($filter = [])
    {
        $this->db->select('dashboard, username');
        $this->db->where($filter);
        $query = $this->db->get($this->userCollectionName);
        $result = $query->result_array();

        return $result;
    }

    public function add_widget($data = [])
    {
        if (!is_array($data)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }
        $username = $data['username'];
        $dashId = $this->get_user_selection(['username' => $username]);
        $id = $dashId[0]['dashboard'];
        if ($id) {
            $objTmp = $this->get_all_dashboards(['id' => $id]);

            if ($objTmp) {
                $dashboard = $objTmp[0];
                $widgets = $dashboard->getWidgets();
                // check for duplicates
                if (!in_array($data['id'], $widgets, true)) {
                    array_push($widgets, $data['id']);
                }

                $dashboard->setWidgets($widgets);
                $dasharray = (array) $dashboard;

                // TURNS ARRAY INTO COMMA STRING , MIGHT NEED TO FIX
                $comma_separated = implode(',', $dasharray['widgets']);

                $dasharray['widgets'] = $comma_separated;
                $savedDash = $this->save_dashboard($dasharray, true);
                return $savedDash;
            } else {
                // shouldn't happen
                throw new Exception($this->lang->line('dashboard_widget_unable_to_load'));
            }
        } else {
            throw new Exception($this->lang->line('dashboard_widget_unable_to_load'));
        }
    }

    public function clone_dashboard(array $data, int $id)
    {
        if ($this->validate($data, false) === false) {
            $message = $this->getErrors();
            $errors = array_map(function ($msg) {
                return $msg['text'];
            }, $message);
            throw new Exception(implode(PHP_EOL, $errors));
        }

        // check if dashboard is public
        $dashboards = $this->get_public_dashboards(['id' => $id]);
        if (empty($dashboards)) {
            throw new Exception('Dashboard does not exist or you are trying to copy non public dashboard.');
        }

        try {
            $this->db->trans_start();

            unset($data['id'], $data['widgets']);
            if (isset($data['sharedWith'])) {
                $data['sharedWith'] = json_encode($data['sharedWith']);
            }
            $this->db->insert($this->collectionName, $data);
            $clonedDashboardId = $this->db->insert_id();

            $widgetsToClone = $dashboards[0]->widgets;
            $this->db->where_in('id', $widgetsToClone);
            $query = $this->db->get($this->widgetsCollectionName);

            // clone widgets
            foreach ($query->result_array() as $widget) {
                $originID = $widget['id'];
                unset($widget['id']);
                $widget['dashboardid'] = $clonedDashboardId;
                $this->db->insert($this->widgetsCollectionName, $widget);
                $insertedId = $this->db->insert_id();
                //replace old widgets IDs with new to preserve widget's order
                $widgetsToClone[array_search($originID, $widgetsToClone)] = $insertedId;

                $alerts = $this->db->get_where($this->alertsCollectionName, ['widgetid' => $originID])->result_array();

                // clone alerts
                foreach ($alerts as $alert) {
                    unset($alert['id']);
                    $alert['widgetid'] = $insertedId;
                    $this->db->insert($this->alertsCollectionName, $alert);
                }
            }

            // set new widgets ID to dashboard
            $this->db->update(
                $this->collectionName,
                ['widgets' => implode(',', $widgetsToClone)],
                ['id' => $clonedDashboardId],
            );
            $this->db->trans_commit();
        } catch (Exception $exception) {
            $this->db->trans_rollback();
            throw $exception;
        }

        return $clonedDashboardId;
    }

    public function delete_widget($data = [])
    {
        if (!is_array($data)) {
            $this->setError('Invalid parameter passed to function');
            return false;
        }

        $dashId = $this->get_user_selection(['username' => $data['username']]);
        $dashboards = $this->get_all_dashboards(['id' => $dashId[0]['dashboard']]);

        foreach ($dashboards as $dashboard) {
            $widgets = $dashboard->getWidgets();
            if (($key = array_search($data['id'], $widgets)) !== false) {
                unset($widgets[$key]);
                $dashboard->setWidgets($widgets);

                $dasharray = (array) $dashboard;

                // TURNS ARRAY INTO COMMA STRING , MIGHT NEED TO FIX
                $comma_separated = implode(',', $dasharray['widgets']);

                $dasharray['widgets'] = $comma_separated;
                $savedDash = $this->save_dashboard($dasharray, true);
            }
        }

        return;
    }

    /*
     * validate the dashboard data before saving
     */
    public function validate($data, $overwrite = false, $is_admin = false)
    {
        $valid = true;

        if (empty($data['username']) || trim($data['username']) == '') {
            $valid = false;
            $this->setError($this->lang->line('dashboard_empty_user_name'));
            return false;
        }

        if (empty($data['name']) || trim($data['name']) == '') {
            $valid = false;
            $this->setError($this->lang->line('dashboard_empty_name'));
            return false;
        }

        //if name already exists and user is owner of this dashboard - do not allow to save
        if ($overwrite == false && $this->_nameAlreadyExist($data) === true && $this->isOwner($data) === true) {
            $this->clearErrors();
            $this->setError($this->lang->line('dashboard_already_exists'), 422); //422 Unprocessable Entity
            $valid = false;
        }

        return $valid;
    }

    /*
     * check that there is not already a dashboard with that name in DB
     */
    public function _nameAlreadyExist($data)
    {
        $alreadyExist = false;
        $filter = $this->defaultFilter;
        $filter['name'] = $data['name'];

        $result = $this->get_all_dashboards($filter);

        if (!empty($result)) {
            $alreadyExist = true;
        }

        return $alreadyExist;
    }

    public function isOwner($data)
    {
        $filter = ['name' => $data['name'], 'username' => $data['username']];
        $dashboard = $this->get_all_dashboards($filter);
        return empty($dashboard) ? false : true;
    }

    //************************************
    //
    // TODO: This functions should be revieed and replaced in all project if valuable
    // right now this is a copy/paste from save_search model
    //________________________________________________________

    public function setError($errorText, $errorCode = 406, $level = 'ERROR')
    {
        $i = count($this->errors);
        $this->errors[$i]['text'] = $errorText;
        $this->errors[$i]['errorCode'] = $errorCode;
    }

    public function getErrors($level = 'ERROR')
    {
        return $this->errors;
    }

    public function clearErrors()
    {
        $this->errors = [];
    }

    // END
    // ________________________________________________________
}
