<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Services;

use Sso\WebserviceBundle\Api\ApiManagerInterface;
use InvalidArgumentException;

/**
 * Class CommandHandler
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\WebserviceBundle\Services
 */
final class CommandHandler implements CommandHandlerInterface
{
    /**
     * @var ApiManagerInterface
     */
    private $apiManager;

    /**
     * @var CommandInflectorInterface
     */
    private $commandInflector;

    /**
     * @var ApiVersionInflectorInterface
     */
    private $apiVersionInflector;

    /**
     * CommandHandler constructor.
     * @param ApiManagerInterface $apiManager
     * @param CommandInflectorInterface $commandInflector
     * @param ApiVersionInflectorInterface $apiVersionInflector
     */
    public function __construct(
        ApiManagerInterface $apiManager,
        CommandInflectorInterface $commandInflector,
        ApiVersionInflectorInterface $apiVersionInflector
    ) {
        $this->apiManager = $apiManager;
        $this->commandInflector = $commandInflector;
        $this->apiVersionInflector = $apiVersionInflector;
    }

    /**
     * @param CommandInterface $command
     * @param RequestInterface $request
     * @return ResponseBuilderInterface
     */
    public function handle(CommandInterface $command, RequestInterface $request)
    {
        return $this->getHandler($command)->handle($request);
    }

    /**
     * @param CommandInterface $command
     * @throws InvalidArgumentException
     * @return HandlerInterface
     */
    private function getHandler(CommandInterface $command)
    {
        $factory = $this->getFactory($command);

        $handler = $factory->handler();
        if (!($handler instanceof HandlerInterface)) {
            throw new InvalidArgumentException(sprintf(
                'Class "%s" must implement "%s" interface!',
                get_class($handler),
                HandlerInterface::class
            ));
        }

        return $handler;
    }

    /**
     * @param CommandInterface $command
     * @throws InvalidArgumentException
     * @return HandlerFactoryInterface
     */
    private function getFactory(CommandInterface $command)
    {
        $className = $this->getFactoryClassName($command);

        $factory = new $className($this->apiManager, $this->apiVersionInflector);
        if (!($factory instanceof HandlerFactoryInterface)) {
            throw new InvalidArgumentException(sprintf(
                'Class "%s" must implement "%s" interface!',
                $className,
                HandlerFactoryInterface::class
            ));
        }

        return $factory;
    }

    /**
     * @param CommandInterface $command
     * @throws InvalidArgumentException
     * @return string
     */
    private function getFactoryClassName(CommandInterface $command)
    {
        $className = $this->commandInflector->inflect($command);

        if (!class_exists($className)) {
            throw new InvalidArgumentException(sprintf(
                'Handler factory class "%s" does not exist!',
                $className
            ));
        }

        return $className;
    }
}
