<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\TaskBundle\Workflow;

use Chill\TaskBundle\Entity\AbstractTask;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
use Symfony\Component\Workflow\WorkflowInterface;

class TaskWorkflowManager implements WorkflowSupportStrategyInterface
{
    /**
     * @var TaskWorkflowDefinition[]
     */
    protected $definitions = [];

    public function addDefinition(TaskWorkflowDefinition $definition)
    {
        $this->definitions[] = $definition;
    }

    /**
     * @return TaskWorkflowDefinition
     *
     * @throws \LogicException
     */
    public function getTaskWorkflowDefinition(AbstractTask $task)
    {
        $definitions = [];

        foreach ($this->definitions as $tested) {
            if ($tested->supports($task)) {
                $definitions[] = $tested;
            }
        }

        $count = \count($definitions);

        if (1 < $count) {
            throw new \LogicException('More than one TaskWorkflowDefinition supports this task. This should not happens.');
        }

        if (0 === $count) {
            throw new \LogicException(\sprintf('No taskWorkflowDefinition supports this task type: %s (task id: %s).', $task->getType(), $task->getId()));
        }

        return $definitions[0];
    }

    public function getWorkflowMetadata(AbstractTask $task, string $key, $metadataSubject = null, ?string $name = null)
    {
        return $this->getTaskWorkflowDefinition($task)
            ->getWorkflowMetadata($task, $key, $metadataSubject);
    }

    public function onTaskStateEntered(Event $e)
    {
        $task = $e->getSubject();

        $definition = $this->getTaskWorkflowDefinition($task);

        $task->setClosed($definition->isClosed($task));
    }

    public function supports(WorkflowInterface $workflow, $subject): bool
    {
        if (!$subject instanceof AbstractTask) {
            return false;
        }

        return $workflow->getName() === $this
            ->getTaskWorkflowDefinition($subject)->getAssociatedWorkflowName();
    }
}
