<?php

namespace App\Tests\Service;

use App\Entity\AuditLog;
use App\Service\AuditService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\User\UserInterface;

class AuditServiceTest extends TestCase
{
    public function testLogCreatesAuditLogWithUserAndIp(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $security = $this->createMock(Security::class);
        $requestStack = $this->createMock(RequestStack::class);

        $user = $this->createMock(UserInterface::class);
        $user->method('getUserIdentifier')->willReturn('admin@example.com');
        $security->method('getUser')->willReturn($user);

        $request = Request::create('/test', 'POST', [], [], [], ['REMOTE_ADDR' => '192.168.1.1']);
        $requestStack->method('getCurrentRequest')->willReturn($request);

        $persisted = null;
        $em->expects($this->once())->method('persist')->with($this->callback(function (AuditLog $log) use (&$persisted) {
            $persisted = $log;

            return true;
        }));
        $em->expects($this->once())->method('flush');

        $service = new AuditService($em, $security, $requestStack);
        $service->log('order_created', 'BilletBuyer', 42, ['key' => 'value']);

        self::assertSame('order_created', $persisted->getAction());
        self::assertSame('BilletBuyer', $persisted->getEntityType());
        self::assertSame(42, $persisted->getEntityId());
        self::assertSame(['key' => 'value'], $persisted->getData());
        self::assertSame('admin@example.com', $persisted->getPerformedBy());
        self::assertSame('192.168.1.1', $persisted->getIpAddress());
    }

    public function testLogWithoutUser(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $security = $this->createMock(Security::class);
        $requestStack = $this->createMock(RequestStack::class);

        $security->method('getUser')->willReturn(null);
        $requestStack->method('getCurrentRequest')->willReturn(Request::create('/'));

        $persisted = null;
        $em->expects($this->once())->method('persist')->with($this->callback(function (AuditLog $log) use (&$persisted) {
            $persisted = $log;

            return true;
        }));
        $em->expects($this->once())->method('flush');

        $service = new AuditService($em, $security, $requestStack);
        $service->log('test_action', 'TestEntity');

        self::assertNull($persisted->getPerformedBy());
        self::assertNull($persisted->getEntityId());
    }

    public function testLogWithoutRequest(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $security = $this->createMock(Security::class);
        $requestStack = $this->createMock(RequestStack::class);

        $security->method('getUser')->willReturn(null);
        $requestStack->method('getCurrentRequest')->willReturn(null);

        $persisted = null;
        $em->expects($this->once())->method('persist')->with($this->callback(function (AuditLog $log) use (&$persisted) {
            $persisted = $log;

            return true;
        }));
        $em->expects($this->once())->method('flush');

        $service = new AuditService($em, $security, $requestStack);
        $service->log('cron_action', 'System');

        self::assertNull($persisted->getIpAddress());
        self::assertNull($persisted->getPerformedBy());
    }

    public function testLogWithEmptyData(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $security = $this->createMock(Security::class);
        $requestStack = $this->createMock(RequestStack::class);

        $security->method('getUser')->willReturn(null);
        $requestStack->method('getCurrentRequest')->willReturn(null);

        $persisted = null;
        $em->expects($this->once())->method('persist')->with($this->callback(function (AuditLog $log) use (&$persisted) {
            $persisted = $log;

            return true;
        }));

        $service = new AuditService($em, $security, $requestStack);
        $service->log('simple', 'Entity', 1);

        self::assertSame([], $persisted->getData());
    }
}
