<?php

namespace League\Tactician\Plugins;

use League\Tactician\Middleware;

/**
 * If another command is already being executed, locks the command bus and
 * queues the new incoming commands until the first has completed.
 */
class LockingMiddleware implements Middleware
{
    /**
     * @var bool
     */
    private $isExecuting;

    /**
     * @var callable[]
     */
    private $queue = [];

    /**
     * Execute the given command... after other running commands are complete.
     *
     * @param object   $command
     * @param callable $next
     *
     * @throws \Throwable
     *
     * @return mixed|void
     */
    public function execute($command, callable $next)
    {
        $this->queue[] = function () use ($command, $next) {
            return $next($command);
        };

        if ($this->isExecuting) {
            return;
        }
        $this->isExecuting = true;

        try {
            $returnValue = $this->executeQueuedJobs();
        } catch (\Throwable $e) {
            $this->isExecuting = false;
            $this->queue = [];
            throw $e;
        }

        $this->isExecuting = false;
        return $returnValue;
    }

    /**
     * Process any pending commands in the queue. If multiple, jobs are in the
     * queue, only the first return value is given back.
     *
     * @return mixed
     */
    protected function executeQueuedJobs()
    {
        $returnValues = [];
        while ($resumeCommand = array_shift($this->queue)) {
            $returnValues[] = $resumeCommand();
        }

        return array_shift($returnValues);
    }
}
