<?php

namespace App\Tests\EventSubscriber;

use App\Entity\User;
use App\EventSubscriber\SuspendedUserSubscriber;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class SuspendedUserSubscriberTest extends TestCase
{
    private Security $security;
    private UrlGeneratorInterface $urlGenerator;
    private SuspendedUserSubscriber $subscriber;

    protected function setUp(): void
    {
        $this->security = $this->createMock(Security::class);
        $this->urlGenerator = $this->createMock(UrlGeneratorInterface::class);
        $this->subscriber = new SuspendedUserSubscriber(
            $this->security,
            $this->urlGenerator,
            'admin@example.com',
        );
    }

    private function createEvent(string $route, int $requestType = HttpKernelInterface::MAIN_REQUEST): RequestEvent
    {
        $flashBag = $this->createMock(FlashBagInterface::class);
        $session = $this->createMock(Session::class);
        $session->method('getFlashBag')->willReturn($flashBag);

        $request = new Request();
        $request->attributes->set('_route', $route);
        $request->setSession($session);

        $kernel = $this->createMock(HttpKernelInterface::class);

        return new RequestEvent($kernel, $request, $requestType);
    }

    public function testSubscribedEvents(): void
    {
        $events = SuspendedUserSubscriber::getSubscribedEvents();

        self::assertArrayHasKey(KernelEvents::REQUEST, $events);
        self::assertSame(['onKernelRequest', 8], $events[KernelEvents::REQUEST]);
    }

    public function testSkipsSubRequest(): void
    {
        $this->security->expects(self::never())->method('getUser');

        $event = $this->createEvent('app_dashboard', HttpKernelInterface::SUB_REQUEST);
        $this->subscriber->onKernelRequest($event);

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

    public function testSkipsWhenNoUser(): void
    {
        $this->security->method('getUser')->willReturn(null);

        $event = $this->createEvent('app_dashboard');
        $this->subscriber->onKernelRequest($event);

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

    public function testSkipsWhenUserNotSuspended(): void
    {
        $user = new User();
        $user->setIsSuspended(false);
        $this->security->method('getUser')->willReturn($user);

        $event = $this->createEvent('app_dashboard');
        $this->subscriber->onKernelRequest($event);

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

    public function testSkipsWhenUserSuspendedNull(): void
    {
        $user = new User();
        $this->security->method('getUser')->willReturn($user);

        $event = $this->createEvent('app_dashboard');
        $this->subscriber->onKernelRequest($event);

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

    #[\PHPUnit\Framework\Attributes\DataProvider('allowedRoutesProvider')]
    public function testSkipsAllowedRoutesForSuspendedUser(string $route): void
    {
        $user = new User();
        $user->setIsSuspended(true);
        $this->security->method('getUser')->willReturn($user);

        $event = $this->createEvent($route);
        $this->subscriber->onKernelRequest($event);

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

    /**
     * @return iterable<string, array{string}>
     */
    public static function allowedRoutesProvider(): iterable
    {
        yield 'logout' => ['app_logout'];
        yield 'home' => ['app_home'];
        yield 'login' => ['app_login'];
    }

    public function testRedirectsSuspendedUserOnProtectedRoute(): void
    {
        $user = new User();
        $user->setIsSuspended(true);
        $this->security->method('getUser')->willReturn($user);

        $this->urlGenerator->method('generate')
            ->with('app_home')
            ->willReturn('/');

        $flashBag = $this->createMock(FlashBagInterface::class);
        $flashBag->expects(self::once())
            ->method('add')
            ->with('error', 'Votre compte a ete suspendu. Contactez admin@example.com.');

        $session = $this->createMock(Session::class);
        $session->method('getFlashBag')->willReturn($flashBag);

        $request = new Request();
        $request->attributes->set('_route', 'app_dashboard');
        $request->setSession($session);

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

        $this->subscriber->onKernelRequest($event);

        $response = $event->getResponse();
        self::assertInstanceOf(RedirectResponse::class, $response);
        self::assertSame('/', $response->getTargetUrl());
    }
}
