<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonPickwareCommon\Components;

use Doctrine\Common\Collections\ArrayCollection;
use Shopware\Components\DependencyInjection\Container;
use Shopware\Plugins\ViisonPickwareCommon\Classes\ApiRequestCompatibility\Layer;

class ApiRequestCompatibilityManager
{
    /**
     * The name of the event, which is fired to collect all required compatibility layers for an API request.
     */
    const EVENT_COLLECT_API_REQUEST_COMPATIBILITY_LAYERS = 'Shopware_Plugins_ViisonPickware_CollectAPIRequestCompatibilityLayers';

    /**
     * @var \Enlight_Event_EventManager $eventManager;
     */
    private $eventManager;

    /**
     * @param \Shopware\Components\DependencyInjection\Container $container
     */
    public function __construct(Container $container)
    {
        $this->eventManager = $container->get('events');
    }

    /**
     * Determines whether the given request is compatible or needs to be routed through
     * the compatibility layers.
     *
     * @param \Enlight_Controller_Request_Request $request
     * @return boolean
     */
    public function isRequestCompatible(\Enlight_Controller_Request_Request $request)
    {
        return $request->getModuleName() !== 'api' || $this->getEntryLayerForRequest($request) === null;
    }

    /**
     * Determines the API version required by the request and uses it to collect all compatibility
     * layers that need to be applied on the request. The collected filters are filtered, sorted
     * and finally linked together, before the entry layer is returned.
     *
     * Note: The returned entry layer has not been configured yet. This must be done before
     *       dispatching the request to it.
     *
     * @param \Enlight_Controller_Request_Request $request
     * @return boolean
     */
    public function getEntryLayerForRequest(\Enlight_Controller_Request_Request $request)
    {
        if ($request->getModuleName() !== 'api') {
            return null;
        }

        // Determine the required API version and try to collect respective compatibility layers
        $requiredVersion = ($request->getHeader('Pickware-API-Version')) ?: date('Y-m-d');
        $compatibilityLayers = $this->eventManager->collect(
            static::EVENT_COLLECT_API_REQUEST_COMPATIBILITY_LAYERS,
            new ArrayCollection(),
            [
                'controllerName' => $request->getParam('controller'),
                'requiredVersion' => $requiredVersion,
            ]
        );
        $compatibilityLayers = $compatibilityLayers->toArray();

        // Make sure all returned layers are subclasses of the base 'Layer'
        $compatibilityLayers = array_filter($compatibilityLayers, function ($layer) {
            return $layer instanceof Layer;
        });
        if (empty($compatibilityLayers)) {
            return null;
        }

        // Sort all layers by their version and position
        usort($compatibilityLayers, function ($lhs, $rhs) {
            // Sort by version
            $versionOrder = version_compare($lhs->getVersion(), $rhs->getVersion());
            if ($versionOrder !== 0) {
                return $versionOrder;
            }

            // Equal versions, hence sort by position
            return version_compare($lhs->getPosition(), $rhs->getPosition());
        });

        // Link layers starting at entry layer
        for ($i = 0; $i < (count($compatibilityLayers) - 1); $i++) {
            $compatibilityLayers[$i]->setSuccessor($compatibilityLayers[$i + 1]);
        }

        return $compatibilityLayers[0];
    }
}
