<?php

namespace App\Tests\EventListener;

use App\EventListener\ExceptionListener;
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\HttpKernelInterface;
use Twig\Environment;

class ExceptionListenerTest extends TestCase
{
    public function testDoesNothingInDevEnvironment(): void
    {
        $twig = $this->createMock(Environment::class);
        $twig->expects(self::never())->method('render');

        $listener = new ExceptionListener($twig, 'dev');
        $event = $this->createEvent(new \RuntimeException('test'));

        $listener($event);

        self::assertNull($event->getResponse());
    }

    public function testDoesNothingInTestEnvironment(): void
    {
        $twig = $this->createMock(Environment::class);
        $twig->expects(self::never())->method('render');

        $listener = new ExceptionListener($twig, 'test');
        $event = $this->createEvent(new \RuntimeException('test'));

        $listener($event);

        self::assertNull($event->getResponse());
    }

    public function testRenders404Template(): void
    {
        $twig = $this->createMock(Environment::class);
        $twig->expects(self::once())
            ->method('render')
            ->with('error/404.html.twig', ['status_code' => 404, 'error_message' => 'Not found'])
            ->willReturn('<h1>404</h1>');

        $listener = new ExceptionListener($twig, 'prod');
        $event = $this->createEvent(new NotFoundHttpException('Not found'));

        $listener($event);

        self::assertNotNull($event->getResponse());
        self::assertSame(404, $event->getResponse()->getStatusCode());
        self::assertSame('<h1>404</h1>', $event->getResponse()->getContent());
    }

    public function testRenders500TemplateForGenericException(): void
    {
        $twig = $this->createMock(Environment::class);
        $twig->expects(self::once())
            ->method('render')
            ->with('error/500.html.twig', ['status_code' => 500, 'error_message' => 'Server error'])
            ->willReturn('<h1>500</h1>');

        $listener = new ExceptionListener($twig, 'prod');
        $event = $this->createEvent(new \RuntimeException('Server error'));

        $listener($event);

        self::assertNotNull($event->getResponse());
        self::assertSame(500, $event->getResponse()->getStatusCode());
    }

    public function testRenders500TemplateForOtherHttpCodes(): void
    {
        $twig = $this->createMock(Environment::class);
        $twig->expects(self::once())
            ->method('render')
            ->with('error/500.html.twig', ['status_code' => 403, 'error_message' => 'Forbidden'])
            ->willReturn('<h1>403</h1>');

        $listener = new ExceptionListener($twig, 'prod');
        $event = $this->createEvent(new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException('Forbidden'));

        $listener($event);

        self::assertNotNull($event->getResponse());
        self::assertSame(403, $event->getResponse()->getStatusCode());
    }

    private function createEvent(\Throwable $exception): ExceptionEvent
    {
        $kernel = $this->createMock(HttpKernelInterface::class);

        return new ExceptionEvent($kernel, new Request(), HttpKernelInterface::MAIN_REQUEST, $exception);
    }
}
