<?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\ViisonCommon\Subscribers\Api;

use Shopware\Plugins\ViisonCommon\Classes\Subscribers\AbstractBaseSubscriber;
use Shopware\Plugins\ViisonCommon\Classes\ApiException;
use Shopware\Plugins\ViisonCommon\Components\ExceptionTranslation\LocalizableThrowable;
use Shopware\Plugins\ViisonCommon\Components\LocalizedApiSnippetManager;

/**
 * A subscriber that checks if an ApiException was thrown from an API method and if so, the specified localized error
 * message and returns the specified HTTP status code.
 */
class Error extends AbstractBaseSubscriber
{
    /**
     * @var string
     */
    private $controllerName;

    /**
     * @see \Shopware\Plugins\ViisonCommon\Classes\Subscribers\Base::getSubscribedEvents()
     */
    public static function getSubscribedEvents()
    {
        return [
            'Shopware_Controllers_Api_Error::errorAction::after' => 'onAfterErrorAction',
            // This listener needs to run before \Shopware\Plugins\ViisonPickwareCommon\Subscribers\Api\Api::onDispatchLoopStartup
            // because we need the real controller name, which is modified by that Subscriber
            'Enlight_Controller_Front_DispatchLoopStartup' => [
                'onDispatchLoopStartup',
                -5,
            ],
        ];
    }

    /**
     * Saves the invoked controller name for later.
     *
     * @param \Enlight_Controller_EventArgs $args
     */
    public function onDispatchLoopStartup(\Enlight_Controller_EventArgs $args)
    {
        // Only modify API requests
        $request = $args->getRequest();
        if ($request->getModuleName() !== 'api') {
            return;
        }

        // Remember the originally requested controller name because we need that for localization
        $this->controllerName = $request->getControllerName();
    }

    /**
     * Returns a localized error view if an ApiException was thrown.
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onAfterErrorAction(\Enlight_Hook_HookArgs $args)
    {
        /** @var \Shopware_Controllers_Api_Error $subject */
        $subject = $args->getSubject();
        $error = $subject->Request()->getParam('error_handler');

        /** @var \Exception $exception */
        $exception = $error->exception;
        $request = $subject->Request();
        $isRelevantException = ($exception instanceof ApiException) || ($exception instanceof LocalizableThrowable);
        if (!$isRelevantException || $request->getModuleName() !== 'api') {
            // Not a relevant case
            return;
        }

        if (method_exists($exception, 'getHttpStatus')) {
            $subject->Response()->setHttpResponseCode($exception->getHttpStatus());
        }
        $subject->View()->assign(
            [
                'success' => false,
                'message' => $this->getLocalizedMessage($exception, $request),
            ]
        );
    }

    /**
     * Returns the localized message for $exception.
     *
     * @param ApiException $exception
     * @param \Enlight_Controller_Request_RequestHttp $request
     * @return string
     */
    private function getLocalizedMessage($exception, $request)
    {
        /** @var LocalizedApiSnippetManager $apiSnippetsManager */
        $apiSnippetsManager = \Enlight_Class::Instance(
            'Shopware\\Plugins\\ViisonCommon\\Components\\LocalizedApiSnippetManager',
            [
                $this->getContainer(),
                $request,
            ]
        );

        if ($exception instanceof LocalizableThrowable) {
            return $exception->getLocalizedMessage($apiSnippetsManager);
        }

        $namespace = ApiException::NAMESPACE_PREFIX . $this->controllerName;
        $messageId = $exception->getMessage();
        $localizedMessage = $apiSnippetsManager->getNamespace($namespace)->get($messageId);

        return $localizedMessage ?: $messageId;
    }
}
