<?php

declare(strict_types=1);

namespace App\Tests\Service;

use App\Entity\AuditLog;
use App\Service\AuditLogger;
use App\Service\UserAgentParser;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * @internal
 */
final class AuditLoggerTest extends TestCase
{
    public function testCapturesRequestContext(): void
    {
        $persisted = null;
        $em        = $this->createStub(EntityManagerInterface::class);
        $em->method('persist')->willReturnCallback(static function (object $entity) use (&$persisted): void {
            $persisted = $entity;
        });

        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn(null); // -> username "system"

        $request = Request::create('/api/work_hours', 'POST');
        $request->server->set('REMOTE_ADDR', '203.0.113.7');
        $request->headers->set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
        $request->headers->set('X-Device-Id', 'device-abc');

        $stack = new RequestStack();
        $stack->push($request);

        $logger = new AuditLogger($em, $security, $stack, new UserAgentParser());
        $logger->log(null, 'create', 'work_hour', 1, 'desc');

        self::assertInstanceOf(AuditLog::class, $persisted);
        self::assertSame('203.0.113.7', $persisted->getIpAddress());
        self::assertSame('device-abc', $persisted->getDeviceId());
        self::assertSame('Ordinateur · Windows · Chrome', $persisted->getDeviceLabel());
        self::assertNotNull($persisted->getUserAgent());
    }

    public function testTruncatesOverlongDeviceId(): void
    {
        $persisted = null;
        $em        = $this->createStub(EntityManagerInterface::class);
        $em->method('persist')->willReturnCallback(static function (object $entity) use (&$persisted): void {
            $persisted = $entity;
        });
        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn(null);

        $request = Request::create('/api/work_hours', 'POST');
        $request->headers->set('X-Device-Id', str_repeat('x', 200));
        $stack = new RequestStack();
        $stack->push($request);

        $logger = new AuditLogger($em, $security, $stack, new UserAgentParser());
        $logger->log(null, 'create', 'work_hour', 1, 'desc');

        self::assertSame(64, mb_strlen((string) $persisted->getDeviceId()));
    }

    public function testNoRequestLeavesContextNull(): void
    {
        $persisted = null;
        $em        = $this->createStub(EntityManagerInterface::class);
        $em->method('persist')->willReturnCallback(static function (object $entity) use (&$persisted): void {
            $persisted = $entity;
        });
        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn(null);

        $logger = new AuditLogger($em, $security, new RequestStack(), new UserAgentParser());
        $logger->log(null, 'create', 'work_hour', 1, 'desc');

        self::assertNull($persisted->getIpAddress());
        self::assertNull($persisted->getUserAgent());
        self::assertNull($persisted->getDeviceLabel());
        self::assertNull($persisted->getDeviceId());
    }
}
