<?php
/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Enterprise License (PEL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GPLv3 and PEL
 */

namespace Pimcore\Bundle\CoreBundle\EventListener;

use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
use Pimcore\Config;
use Pimcore\Http\Exception\ResponseException;
use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
use Pimcore\Model\Document;
use Pimcore\Model\Site;
use Pimcore\Templating\Renderer\ActionRenderer;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\KernelEvents;

class ResponseExceptionListener implements EventSubscriberInterface
{
    use LoggerAwareTrait;
    use PimcoreContextAwareTrait;

    /**
     * @var ActionRenderer
     */
    protected $actionRenderer;

    /**
     * @var bool
     */
    protected $renderErrorPage = true;

    /**
     * @param ActionRenderer $actionRenderer
     * @param bool $renderErrorPage
     */
    public function __construct(ActionRenderer $actionRenderer, $renderErrorPage = true)
    {
        $this->actionRenderer  = $actionRenderer;
        $this->renderErrorPage = (bool)$renderErrorPage;
    }

    /**
     * @inheritDoc
     */
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::EXCEPTION => 'onKernelException'
        ];
    }

    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();

        // handle ResponseException (can be used from any context)
        if ($exception instanceof ResponseException) {
            $event->setResponse($exception->getResponse());

            // a response was explicitely set -> do not continue to error page
            return;
        }

        // further checks are only valid for default context
        $request = $event->getRequest();
        if ($this->matchesPimcoreContext($request, PimcoreContextResolver::CONTEXT_DEFAULT)) {
            if ($this->renderErrorPage) {
                $this->handleErrorPage($event);
            }
        }
    }

    protected function handleErrorPage(GetResponseForExceptionEvent $event)
    {
        if (\Pimcore::inDebugMode() || PIMCORE_DEVMODE) {
            return;
        }

        $exception = $event->getException();

        $statusCode = 500;
        $headers    = [];

        if ($exception instanceof HttpExceptionInterface) {
            $statusCode = $exception->getStatusCode();
            $header     = $exception->getHeaders();
        }

        $errorPath = Config::getSystemConfig()->documents->error_pages->default;

        if (Site::isSiteRequest()) {
            $site      = Site::getCurrentSite();
            $errorPath = $site->getErrorDocument();
        }

        if (empty($errorPath)) {
            $errorPath = '/';
        }

        $document = Document::getByPath($errorPath);

        if (!$document instanceof Document\Page) {
            // default is home
            $document = Document::getById(1);
        }

        try {
            $response = $this->actionRenderer->render($document);
        } catch (\Exception $e) {
            // we are even not able to render the error page, so we send the client a unicorn
            $response = 'Page not found. 🦄';
            $this->logger->emergency('Unable to render error page, exception thrown');
            $this->logger->emergency($e);
        }

        $event->setResponse(new Response($response, $statusCode, $headers));
    }
}
