<?php

namespace Modules\Mall\Providers;

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\StateMachine;
use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;

class WorkflowRegistry
{

    /**
     * @var Registry
     */
    protected Registry $registry;

    /**
     * @var array
     */
    protected array $config;

    /**
     * @var EventDispatcher
     */
    protected EventDispatcher $dispatcher;

    public function __construct(array $config)
    {
        $this->registry   = new Registry();
        $this->config     = $config;
        $this->dispatcher = new EventDispatcher();

        foreach ($this->config as $name => $workflowData) {
            $this->addFromArray($name, $workflowData);
        }
    }

    /**
     * Notes   : 添加一个工作流
     * @Date   : 2021/5/8 3:48 下午
     * @Author : < Jason.C >
     * @param  Workflow  $workflow
     * @param  string    $supportStrategy
     */
    public function add(Workflow $workflow, string $supportStrategy)
    {
        $this->registry->addWorkflow($workflow, new InstanceOfSupportStrategy($supportStrategy));
    }

    /**
     * Notes   : 获取工作流
     * @Date   : 2021/5/8 3:49 下午
     * @Author : < Jason.C >
     * @param  object       $subject
     * @param  string|null  $workflowName
     * @return Workflow
     */
    public function get(object $subject, string $workflowName = null): Workflow
    {
        return $this->registry->get($subject, $workflowName);
    }

    /**
     * 批量添加工作流到对象
     * @param  string  $name
     * @param  array   $workflowData
     */
    public function addFromArray(string $name, array $workflowData)
    {
        $builder = new DefinitionBuilder($workflowData['places']);

        foreach ($workflowData['transitions'] as $transitionName => $transition) {
            foreach ((array) $transition['from'] as $form) {
                $builder->addTransition(new Transition($transitionName, $form, $transition['to']));
            }
        }

        $definition   = $builder->build();
        $markingStore = $this->getMarkingStoreInstance($workflowData);
        $workflow     = $this->getWorkflowInstance($name, $workflowData, $definition, $markingStore);

        foreach ($workflowData['supports'] as $supportedClass) {
            $this->add($workflow, $supportedClass);
        }
    }

    /**
     * Notes   : 根据配置获取工作流或状态机
     * @Date   : 2021/5/8 3:50 下午
     * @Author : < Jason.C >
     * @param  string              $name
     * @param  array               $workflowData
     * @param  Definition          $definition
     * @param  MethodMarkingStore  $markingStore
     * @return Workflow
     */
    protected function getWorkflowInstance(
        string $name,
        array $workflowData,
        Definition $definition,
        MethodMarkingStore $markingStore
    ): Workflow {
        if (isset($workflowData['type']) && $workflowData['type'] === 'state_machine') {
            $className = StateMachine::class;
        } else {
            $className = Workflow::class;
        }

        return new $className($definition, $markingStore, $this->dispatcher, $name);
    }

    /**
     * Notes   : 获取对象存储的实例
     * @Date   : 2021/5/8 3:49 下午
     * @Author : < Jason.C >
     * @param  array  $workflowData
     * @return MethodMarkingStore
     */
    protected function getMarkingStoreInstance(array $workflowData): MethodMarkingStore
    {
        $markingStoreData = $workflowData['marking_store'] ?? [];
        $property         = $markingStoreData['property'] ?? '';

        $singleState = $markingStoreData['type'] === 'single_state';

        return new MethodMarkingStore($singleState, $property);
    }

}