<?php

namespace Velis\Bpm\Ticket;

use Exception;
use Velis\App;
use Velis\Arrays;
use Velis\Bpm\Department;
use Velis\Dictionary;
use Velis\Model\DataObject;
use Velis\Model\RelationLoader\Relations\HasOneRelation;
use Velis\User;
use Velis\User\UserProvider;

/**
 * Category to user assignment model
 * @author Olek Procki <olo@velis.pl>
 */
class CategoryMatrix extends DataObject
{
    const ITEMS_PER_PAGE = 50;
    const ORDER_CATEGORY = 'ticket_category_id, priority';


    /**
     * @return string
     */
    protected function _getTableName()
    {
        return 'app.ticket_category_user_matrix_tab';
    }


    /**
     * Returns primary key fields
     * @return array
     */
    protected function _getPrimaryKeyField()
    {
        return array('ticket_category_id', 'priority');
    }


    /**
     * Returns related ticket category
     * @return \Velis\Bpm\Ticket\Category
     */
    public function getCategory()
    {
        return Category::get($this['ticket_category_id']);
    }

    /**
     * Returns ticket type
     * @return string
     */
    public function getTicketType()
    {
        $types = Dictionary::get('app.ticket_type_tab');
        return $types[$this->getCategory()->ticket_type_id];
    }


    /**
     * Returns assigned user
     * @return User|Department
     */
    public function getOperator()
    {
        if ($this['user_id']) {
            /** @var UserProvider $userProvider */
            $userProvider = App::getService('userProvider');
            $operator = $userProvider->getUser($this['user_id']);
        } elseif ($this['department_id']) {
            $operator = Department::get($this['department_id']);
        }
        return $operator->append($this);
    }

    /**
     * {@inheritDoc}
     */
    public static function getList($page = 1, $params = null, $order = self::ORDER_CATEGORY, $limit = self::ITEMS_PER_PAGE, $fields = null)
    {
        $instance = new static();

        if ($params['priority_greater']) {
            self::$_listConditions[] = ' priority > :priority';
            self::$_listParams['priority'] = $params['priority_greater'];
        }

        if ($params['priority_greater_equal']) {
            self::$_listConditions[] = ' priority >= :priority';
            self::$_listParams['priority'] = $params['priority_greater_equal'];
        }

        $keyFields = [
            'ticket_category_id',
            'ticket_classification_id',
            'user_id',
            'priority',
            'department_id',
            'building_complex_id',
        ];

        foreach (Arrays::toArray($instance->_getPrimaryKeyField()) as $keyField) {
            if (!in_array($keyField, $keyFields)) {
                $keyFields[] = $keyField;
            }
        }

        $bindParams = Arrays::extractFields($params, $keyFields);

        return parent::getList($page, Arrays::filterKeepZeros($bindParams), $order, $limit, $fields);
    }


    /**
     * Removes matrix element
     * @return bool
     */
    public function remove()
    {
        $matrixParams = $this->_getPrimaryKeyParam();
        $matrixParams['priority_greater'] = $this['priority'];
        unset($matrixParams['priority']);
        unset($matrixParams['user_id']);

        $matrix = $this->listAll($matrixParams, 'priority ASC');

        $query  = "UPDATE " . $this->_getTableName() . " SET priority = priority - 1 WHERE 1=1";

        self::$_db->startTrans();
        try {
            if (parent::_remove()) {
                foreach ($matrix as $category) {
                    $params = $category->_getPrimaryKeyParam();
                    $query .= self::$_db->conditions($params);
                    self::$_db->execDML($query, $params);
                }

                self::$_db->commit();
                return true;
            } else {
                self::$_db->commit();
                return false;
            }
        } catch (Exception $e) {
            self::$_db->rollback();
            throw $e;
        }
    }

    /**
     * Adds matrix element
     *
     * @param bool $updateObjectId
     * @return \Velis\Bpm\Ticket\CategoryMatrix
     */
    public function add($updateObjectId = false)
    {
        try {
            $commit = self::$_db->startTrans();
            $this->increasePriority();
            parent::add($updateObjectId);
            if ($commit) {
                self::$_db->commit();
            }
            return $this;
        } catch (Exception $e) {
            if ($commit) {
                self::$_db->rollback();
            }
            throw $e;
        }
    }


    /**
     * Try to increse priority value + 1
     * @return \Velis\Bpm\Ticket\CategoryMatrix
     */
    public function increasePriority()
    {
        $commit = self::$_db->startTrans();

        try {
            if (static::listAll(['ticket_category_id' => $this['ticket_category_id'], 'building_complex_id' => $this['building_complex_id'], 'priority' => $this['priority']])) {
                $matrixParams = $this->_getPrimaryKeyParam();
                $matrixParams['priority_greater_equal'] = $this['priority'];
                unset($matrixParams['priority']);
                unset($matrixParams['user_id']);

                $matrix = $this->listAll($matrixParams, 'priority DESC');

                $query  = "UPDATE " . $this->_getTableName() . " SET priority = priority + 1 WHERE 1=1";

                foreach ($matrix as $category) {
                    $params = $category->_getPrimaryKeyParam();
                    $query .= self::$_db->conditions($params);
                    self::$_db->execDML($query, $params);
                }
            }
            if ($commit) {
                self::$_db->commit();
            }
            return $this;
        } catch (Exception $e) {
            if ($commit) {
                self::$_db->rollback();
            }
            throw $e;
        }
    }

    /**
     * @return HasOneRelation<User>
     */
    public function relationResponsibleUser(): HasOneRelation
    {
        /** @var class-string<User> $userClass */
        $userClass = App::$config->settings->userClass ?: User::class;

        return $this->hasOne($userClass, 'user_id');
    }

    /**
     * @return HasOneRelation<Department>
     */
    public function relationResponsibleDepartment(): HasOneRelation
    {
        return $this->hasOne(Department::class, 'department_id');
    }
}
