<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CmsBundle\Tests\EventListener;

use Contao\CoreBundle\ContaoCoreBundle;
use Contao\CoreBundle\Exception\NoRootPageFoundException;
use Ferienpass\CmsBundle\EventListener\PrettyErrorScreenListener;
use Ferienpass\CmsBundle\Page\PageBuilderFactory;
use Ferienpass\CoreBundle\Settings\MailConfigManager;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Twig\Environment;
use Twig\Loader\LoaderInterface;

/**
 * The pretty error screen is a nicety: when it cannot be rendered, the
 * original throwable has to survive so the visitor still gets the status
 * code they were supposed to get.
 */
final class PrettyErrorScreenListenerTest extends TestCase
{
    public function testKeepsTheNotFoundExceptionWhenThereIsNoRootPageToRenderWith(): void
    {
        $factory = $this->createStub(PageBuilderFactory::class);
        $factory->method('create')->willThrowException(new NoRootPageFoundException('No published root page found to build the page with.'));

        $event = $this->handle(new NotFoundHttpException(), $factory);

        self::assertFalse(
            $event->hasResponse(),
            'Without a root page there is no CMS page to build the 404 screen from; the default error handling takes over.'
        );
        self::assertInstanceOf(
            NotFoundHttpException::class,
            $event->getThrowable(),
            'A missing root page must not turn a 404 (e.g. /robots.txt on an unmapped domain) into a 500.'
        );
    }

    public function testKeepsTheServiceUnavailableExceptionWhenNoTemplateExists(): void
    {
        // 503 has no page handler, so the page handler returns null. Passing
        // that null to ExceptionEvent::setResponse() used to raise a TypeError
        // that replaced the 503 with a 500.
        $event = $this->handle(new ServiceUnavailableHttpException(), $this->createStub(PageBuilderFactory::class));

        self::assertInstanceOf(ServiceUnavailableHttpException::class, $event->getThrowable());
    }

    private function handle(\Throwable $throwable, PageBuilderFactory $pageBuilderFactory): ExceptionEvent
    {
        $loader = $this->createStub(LoaderInterface::class);
        $loader->method('exists')->willReturn(false);

        $twig = $this->createStub(Environment::class);
        $twig->method('getLoader')->willReturn($loader);

        $listener = new PrettyErrorScreenListener(false, $twig, $pageBuilderFactory, $this->createStub(MailConfigManager::class));

        $request = Request::create('https://cbb-grafschaft.de/robots.txt');
        $request->attributes->set('_scope', ContaoCoreBundle::SCOPE_FRONTEND);
        $request->headers->set('Accept', 'text/html');

        $event = new ExceptionEvent($this->createStub(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $throwable);

        $listener($event);

        return $event;
    }
}
