<?php

namespace Velis\Bpm\Workflow;

use InvalidArgumentException;
use User\User;
use Velis\Bpm\Workflow;
use Velis\Exception;
use Velis\Exception as VelisException;
use Velis\Filter;
use Velis\Lang;
use Velis\Model\DataObject;
use Velis\Model\Sanitizable;
use Velis\Output;

/**
 * Workflow event manager
 * @author Olek Procki <olo@velis.pl>
 */
class Action extends DataObject implements Sanitizable
{
    /**
     * Default list order by id ASC
     * @var string
     */
    protected static $_listDefaultOrder = 'workflow_action_id';


    /**
     * Related workflow object
     * @var Workflow
     */
    protected $_workflow;


    /**
     * Action parameters
     * @var array
     */
    protected $_params;


    /**
     * {@inheritDoc}
     */
    protected function _getTableName()
    {
        return 'app.workflow_action_tab';
    }


    /**
     * {@inheritDoc}
     */
    protected function _getListDatasource()
    {
        return 'app.workflow_action';
    }


    /**
     * Returns event name
     * @return string
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getName()
    {
        return trim($this->getType());
    }


    /**
     * Returns workflow action type
     * @return ActionType
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getType()
    {
        return ActionType::get($this->workflow_action_type_id);
    }


    /**
     * Returns decoded params
     * @return array
     */
    public function getParams()
    {
        if (!isset($this->_params)) {
            $this->_params = Output::jsonDecode($this->params) ?: array();
        }
        return $this->_params;
    }


    /**
     * Returns param value
     * @return mixed
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getParamValue($param)
    {
        $ref = $this->getType()->getHandlerReflection();
        $params = $this->getParams();

        if (isset($params['user_id'])) {
            $params['user_id'] = Filter::filterInts($params['user_id']);
        }

        switch ($ref[$param]['type']) {
            case Workflow::PARAM_USER:
                if (User::isCacheable()) {
                    return User::get($params[$param]);
                } else {
                    return User::instance($params[$param]);
                }
                // no break
            default:
                return $params[$param];
        }
    }


    /**
     * Returns true if action has parameter
     *
     * @param string $param
     * @return bool
     */
    public function hasParamValue($param)
    {
        $params = $this->getParams();

        return array_key_exists($param, $params);
    }


    /**
     * Sets workflow instance
     *
     * @param Workflow $workflow
     * @return $this
     */
    public function setWorkflow(Workflow $workflow)
    {
        if ($this['workflow_id'] && $this['workflow_id'] != $workflow->id()) {
            throw new InvalidArgumentException('Invalid workflow assignment');
        }

        $this->_workflow = $workflow;

        return $this;
    }


    /**
     * Returns related workflow object
     * @return Workflow
     */
    public function getWorkflow()
    {
        if (!isset($this->_workflow)) {
            $this->_workflow = Workflow::instance($this->workflow_id);
        }

        return $this->_workflow;
    }


    /**
     * Returns handler object
     * @return Handler|void
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getHandler()
    {
        if ($class = $this->getType()->getHandlerClass()) {
            return new $class($this->getWorkflow(), $this);
        }
    }


    /**
     * @param array|null $params
     * @throws VelisException
     */
    public function setParams(?array $params): void
    {
        if (!$params) {
            return;
        }

        if (isset($params['user_id'])) {
            $params['user_id'] = Filter::filterInts($params['user_id']);
        }

        if ($this['workflow_action_type_id'] != 'TicketAppend') {
            foreach ($params as $value) {
                if (empty($value)) {
                    throw new Exception(Lang::get('GENERAL_NO_DATA'));
                }
            }
        }

        $this['params'] = Output::jsonEncode($params);
    }
}
