<?php

namespace Velis\Bpm\Ticket\Mixin;

use Psr\SimpleCache\InvalidArgumentException;
use Velis\App;
use Velis\Bpm\Ticket\Category;
use Velis\Bpm\Ticket\Classification;
use Velis\Bpm\Ticket\Priority;
use Velis\Bpm\Ticket\Type;
use Velis\Bpm\User;
use Velis\Bpm\Workflow;
use Velis\Dictionary;
use Velis\Label;
use Velis\Lang;

/**
 * Workflow functionality
 * @author Olek Procki <olo@velis.pl>
 */
trait WorkflowTrait
{
    /**
     * @var User|null
     */
    private $invokedByUser = null;

    /**
     * Returns workflow comparable fields reflection
     * (override to extend available field set)
     *
     * @return array
     */
    public static function getWorkflowReflection()
    {
        $ticketTypes = Type::listCached();

        $categoryClass = App::$config->settings->categoryClass ?: Category::class;
        $categories = $categoryClass::getNestedByType();

        if (Classification::hasField('ticket_type_id')) {
            $classifications = [];
            foreach (Classification::listCached() as $classification) {
                $ticketType = (string) $ticketTypes[$classification['ticket_type_id']];
                $classifications[$ticketType][$classification->id()] = $classification;
            }
        } else {
            $classifications = Classification::listCached();
        }

        $statuses = Dictionary::get('app.ticket_status_tab');
        asort($statuses);

        $conditions = [
            'ticket_type_id' => [
                'label'   => Lang::get('WORKFLOW_TICKET_TYPE'),
                'options' => $ticketTypes,
            ],
            'ticket_category_id' => [
                'label'      => Lang::get('WORKFLOW_TICKET_CATEGORY'),
                'options'    => $categories,
                'changeable' => true,
            ],
            'ticket_classification_id' => [
                'label'      => Lang::get('WORKFLOW_TICKET_CLASSIFICATION'),
                'options'    => $classifications,
                'changeable' => true,
            ],
            'ticket_status_id' => [
                'label'      => Lang::get('WORKFLOW_TICKET_STATUS'),
                'options'    => $statuses,
                'changeable' => true,
            ],
            'ticket_priority_id' => [
                'label'      => Lang::get('WORKFLOW_TICKET_PRIORITY'),
                'options'    => Priority::listCached(),
                'changeable' => true,
            ],
            'responsible_user_id' => [
                'label'      => Lang::get('WORKFLOW_TICKET_RESPONSIBLE'),
                'type'       => Workflow::PARAM_USER,
                'changeable' => true,
            ],
            'title' => [
                'label'   => Lang::get('WORKFLOW_TICKET_TITLE'),
                'type'    => Workflow::PARAM_STRING,
            ],
            'description' => [
                'label'   => Lang::get('WORKFLOW_TICKET_DESCRIPTION'),
                'type'    => Workflow::PARAM_STRING,
            ],
        ];

        Workflow::sortConditions($conditions);

        return $conditions;
    }


    /**
     * Returns workflow placeholders
     *
     * @return array
     */
    public static function getWorkflowPlaceholders()
    {
        return [
            '{Title}' => [
                'description' => Lang::get('WORKFLOW_TICKET_TITLE'),
            ],
            '{Description}' => [
                'description' => Lang::get('WORKFLOW_TICKET_DESCRIPTION'),
            ],
            '{Category}' => [
                'description' => Lang::get('WORKFLOW_TICKET_CATEGORY'),
            ],
            '{Company}' => [
                'description' => Lang::get('COMPANY_COMPANY'),
            ],
            '{Classification}' => [
                'description' =>  Lang::get('WORKFLOW_TICKET_CLASSIFICATION'),
            ],
            '{Priority}' => [
                'description' =>  Lang::get('WORKFLOW_TICKET_PRIORITY'),
            ],
            '{TicketNo}' => [
                'description' =>  Lang::get('WORKFLOW_TICKET_NO'),
            ],
            '{Url}' => [
                'description' =>  Lang::get('TICKET_URL'),
            ],
            '{Operator}' => [
                'description' =>  Lang::get('WORKFLOW_TICKET_RESPONSIBLE'),
            ],
            '{Button}' => [
                'description' =>  Lang::get('GENERAL_BUTTON'),
            ],
        ];
    }


    /**
     * Validates category id condition including children categories
     *
     * @param int|int[] $categoryId
     * @return bool
     * @throws InvalidArgumentException
     */
    public function validateTicketCategoryId($categoryId)
    {
        if (!is_array($categoryId)) {
            $categoryId = [$categoryId];
        }

        if (in_array($this->ticket_category_id, $categoryId)) {
            return true;
        }

        $categories = Category::get($categoryId);

        foreach ($categories as $category) {
            if ($subCategories = $category->getChildrenIds(true)) {
                if (in_array($this->ticket_category_id, $subCategories)) {
                    return true;
                }
            }
        }

        return false;
    }


    /**
     * Validates label_id
     *
     * @param int|int[] $labelId
     * @param Workflow $workflow
     * @return bool
     * @throws InvalidArgumentException
     */
    public function validateLabelId($labelId, $workflow)
    {
        if (empty($labelId)) {
            return false;
        }

        if (!is_array($labelId)) {
            $labelId = [$labelId];
        }

        $subjectLabelIds = Label::getCollectionIds($this->getLabels());
        if (!array_intersect($labelId, $subjectLabelIds)) {
            return false;
        }

        $event = $workflow->getEvent();
        if (!in_array($event->id(), ['TicketLabelAdded', 'TicketLabelRemoved'])) {
            return true;
        }

        $eventData = $workflow->getInvokedEvent()->getData();

        if ($eventData instanceof Label) {
            $label = $eventData;
        } elseif (is_array($eventData) && $eventData['label'] instanceof Label) {
            $label = $eventData['label'];
        } else {
            return false;
        }
        return in_array($label->id(), $labelId);
    }

    /**
     * @param User $user
     * @return void
     */
    public function setInvokedByUser($user)
    {
        $this->invokedByUser = $user;
    }

    /**
     * @return User|null
     */
    public function getInvokedByUser()
    {
        return $this->invokedByUser;
    }
}
