<?php

namespace App\Tests\Service;

use App\Entity\AppLog;
use App\Entity\User;
use App\Message\AppLogMessage;
use App\Service\AppLoggerService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;

class AppLoggerServiceTest extends TestCase
{
    public function testLogDispatchesMessage(): void
    {
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects($this->once())
            ->method('dispatch')
            ->with($this->callback(function ($message) {
                return $message instanceof AppLogMessage
                    && 'GET' === $message->method
                    && '/admin/dashboard' === $message->url
                    && 'app_admin_dashboard' === $message->route
                    && 'Consultation du tableau de bord' === $message->action
                    && null === $message->userId;
            }))
            ->willReturn(new Envelope(new \stdClass()));

        $service = new AppLoggerService($bus, $this->createStub(EntityManagerInterface::class), 'test-secret');
        $service->log('GET', '/admin/dashboard', 'app_admin_dashboard');
    }

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

        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects($this->once())
            ->method('dispatch')
            ->with($this->callback(fn ($m) => $m instanceof AppLogMessage && null === $m->userId))
            ->willReturn(new Envelope(new \stdClass()));

        $service = new AppLoggerService($bus, $this->createStub(EntityManagerInterface::class), 'test-secret');
        $service->log('GET', '/admin/clients', 'app_admin_clients_index', $user, '127.0.0.1');
    }

    public function testLogPostAddsSubmission(): void
    {
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects($this->once())
            ->method('dispatch')
            ->with($this->callback(fn ($m) => str_contains($m->action, '(soumission)')))
            ->willReturn(new Envelope(new \stdClass()));

        $service = new AppLoggerService($bus, $this->createStub(EntityManagerInterface::class), 'test-secret');
        $service->log('POST', '/admin/clients/create', 'app_admin_clients_create');
    }

    public function testLogUnknownRoute(): void
    {
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects($this->once())
            ->method('dispatch')
            ->with($this->callback(fn ($m) => 'Acces a app_admin_unknown' === $m->action))
            ->willReturn(new Envelope(new \stdClass()));

        $service = new AppLoggerService($bus, $this->createStub(EntityManagerInterface::class), 'test-secret');
        $service->log('GET', '/admin/unknown', 'app_admin_unknown');
    }

    public function testLogDirectPersistsImmediately(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects($this->once())->method('persist')->with($this->isInstanceOf(AppLog::class));
        $em->expects($this->once())->method('flush');

        $service = new AppLoggerService($this->createStub(MessageBusInterface::class), $em, 'test-secret');
        $service->logDirect('DELETE', '/admin/logs/purge', 'app_admin_logs_purge', 'Suppression de tous les logs');
    }

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

        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects($this->once())->method('persist');
        $em->expects($this->once())->method('flush');

        $service = new AppLoggerService($this->createStub(MessageBusInterface::class), $em, 'test-secret');
        $service->logDirect('DELETE', '/admin/logs/1/delete', 'app_admin_logs_delete', 'Suppression du log #1', $user, '192.168.1.1');
    }

    public function testVerifyLogValid(): void
    {
        $service = new AppLoggerService($this->createStub(MessageBusInterface::class), $this->createStub(EntityManagerInterface::class), 'test-secret');
        $log = new AppLog('GET', '/admin', 'app_admin_dashboard', 'Test', 'test-secret');
        $this->assertTrue($service->verifyLog($log));
    }

    public function testVerifyLogInvalid(): void
    {
        $service = new AppLoggerService($this->createStub(MessageBusInterface::class), $this->createStub(EntityManagerInterface::class), 'test-secret');
        $log = new AppLog('GET', '/admin', 'app_admin_dashboard', 'Test', 'wrong-secret');
        $this->assertFalse($service->verifyLog($log));
    }
}
