<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\EventListener;

use Ferienpass\AdminBundle\EventListener\PrettyErrorScreenListener;
use Ferienpass\CoreBundle\Settings\MailConfigManager;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Twig\Environment;

class PrettyErrorScreenListenerTest extends TestCase
{
    public function testRendersPrettyErrorOnAdminRoute(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $mailConfigManager->method('getAdminEmail')->willReturn('admin@example.com');
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/admin/non-existent');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->once())
            ->method('render')
            ->with('@FerienpassAdmin/page/error/404.html.twig', $this->callback(function ($params) {
                return $params['adminEmail'] === 'admin@example.com' && $params['statusCode'] === 404 && $params['isLoggedIn'] === false;
            }))
            ->willReturn('<html>404</html>');

        $listener($event);

        $this->assertTrue($event->hasResponse());
        $this->assertEquals(404, $event->getResponse()->getStatusCode());
        $this->assertEquals('<html>404</html>', $event->getResponse()->getContent());
    }

    public function testRendersPrettyErrorOnLocalizedAdminRoute(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/en/admin/non-existent');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->once())
            ->method('render')
            ->with('@FerienpassAdmin/page/error/404.html.twig')
            ->willReturn('<html>404</html>');

        $listener($event);

        $this->assertTrue($event->hasResponse());
    }

    public function testRendersPrettyErrorOn3LetterLocaleAdminRoute(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/prs/admin/non-existent');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->once())
            ->method('render')
            ->with('@FerienpassAdmin/page/error/404.html.twig')
            ->willReturn('<html>404</html>');

        $listener($event);

        $this->assertTrue($event->hasResponse());
    }

    public function testRendersPrettyErrorOnAdminRouteLoggedIn(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/admin/non-existent');
        $request->headers->set('Accept', 'text/html');

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

        $security->method('getUser')->willReturn($this->createMock(\Symfony\Component\Security\Core\User\UserInterface::class));

        $twig->expects($this->once())
            ->method('render')
            ->with('@FerienpassAdmin/page/error/404.html.twig', $this->callback(function ($params) {
                return $params['isLoggedIn'] === true;
            }))
            ->willReturn('<html>404 logged in</html>');

        $listener($event);

        $this->assertTrue($event->hasResponse());
        $this->assertEquals('<html>404 logged in</html>', $event->getResponse()->getContent());
    }

    public function testDoesNotRenderOn403(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/admin/forbidden');
        $request->headers->set('Accept', 'text/html');

        $event = new ExceptionEvent(
            $this->createMock(HttpKernelInterface::class),
            $request,
            HttpKernelInterface::MAIN_REQUEST,
            new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException()
        );

        $twig->expects($this->never())->method('render');

        $listener($event);

        $this->assertFalse($event->hasResponse());
    }

    public function testDoesNotRenderOnSimilarButNonAdminRoute(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/administrator');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->never())->method('render');

        $listener($event);

        $this->assertFalse($event->hasResponse());
    }

    public function testDoesNotRenderOnNonAdminRoute(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(false, $twig, $mailConfigManager, $security);

        $request = Request::create('/some-other-route');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->never())->method('render');

        $listener($event);

        $this->assertFalse($event->hasResponse());
    }

    public function testDoesNotRenderInDebugMode(): void
    {
        $twig = $this->createMock(Environment::class);
        $security = $this->createMock(Security::class);
        $mailConfigManager = $this->createMock(MailConfigManager::class);
        $listener = new PrettyErrorScreenListener(true, $twig, $mailConfigManager, $security);

        $request = Request::create('/admin/non-existent');
        $request->headers->set('Accept', 'text/html');

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

        $twig->expects($this->never())->method('render');

        $listener($event);

        $this->assertFalse($event->hasResponse());
    }
}
