<?php

namespace App\Tests\EventListener;

use App\Entity\User;
use App\EventListener\AdminLogListener;
use App\Service\AppLoggerService;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class AdminLogListenerTest extends TestCase
{
    private AppLoggerService $logger;
    private TokenStorageInterface $tokenStorage;
    private AdminLogListener $listener;

    protected function setUp(): void
    {
        $this->logger = $this->createMock(AppLoggerService::class);
        $this->tokenStorage = $this->createStub(TokenStorageInterface::class);
        $this->listener = new AdminLogListener($this->logger, $this->tokenStorage);
    }

    private function createEvent(Request $request, bool $isMainRequest = true): ControllerEvent
    {
        $kernel = $this->createStub(HttpKernelInterface::class);

        return new ControllerEvent(
            $kernel,
            fn () => null,
            $request,
            $isMainRequest ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::SUB_REQUEST,
        );
    }

    public function testLogsAdminRoute(): void
    {
        $request = new Request();
        $request->attributes->set('_route', 'app_admin_dashboard');

        $this->logger->expects($this->once())->method('log');

        $this->listener->__invoke($this->createEvent($request));
    }

    public function testLogsAdminRouteWithUser(): void
    {
        $user = new User();
        $user->setEmail('admin@test.com');
        $user->setFirstName('Admin');
        $user->setLastName('User');
        $user->setPassword('hashed');

        $token = $this->createStub(TokenInterface::class);
        $token->method('getUser')->willReturn($user);
        $this->tokenStorage->method('getToken')->willReturn($token);

        $listener = new AdminLogListener($this->logger, $this->tokenStorage);

        $request = new Request();
        $request->attributes->set('_route', 'app_admin_clients_index');

        $this->logger->expects($this->once())->method('log');

        $listener->__invoke($this->createEvent($request));
    }

    public function testIgnoresNonAdminRoute(): void
    {
        $request = new Request();
        $request->attributes->set('_route', 'app_home');

        $this->logger->expects($this->never())->method('log');

        $this->listener->__invoke($this->createEvent($request));
    }

    public function testIgnoresSubRequest(): void
    {
        $request = new Request();
        $request->attributes->set('_route', 'app_admin_dashboard');

        $this->logger->expects($this->never())->method('log');

        $this->listener->__invoke($this->createEvent($request, false));
    }

    public function testIgnoresAjaxSearch(): void
    {
        $request = new Request();
        $request->attributes->set('_route', 'app_admin_clients_search');
        $request->headers->set('X-Requested-With', 'XMLHttpRequest');

        $this->logger->expects($this->never())->method('log');

        $this->listener->__invoke($this->createEvent($request));
    }

    #[AllowMockObjectsWithoutExpectations]
    public function testLogThrowsDoesNotBlock(): void
    {
        $logger = $this->createStub(AppLoggerService::class);
        $logger->method('log')->willThrowException(new \RuntimeException('DB down'));

        $listener = new AdminLogListener($logger, $this->tokenStorage);

        $request = new Request();
        $request->attributes->set('_route', 'app_admin_dashboard');

        $listener->__invoke($this->createEvent($request));
        $this->addToAssertionCount(1);
    }

    public function testNoTokenReturnsNullUser(): void
    {
        $this->tokenStorage->method('getToken')->willReturn(null);

        $listener = new AdminLogListener($this->logger, $this->tokenStorage);

        $request = new Request();
        $request->attributes->set('_route', 'app_admin_dashboard');

        $this->logger->expects($this->once())->method('log');

        $listener->__invoke($this->createEvent($request));
    }
}
