<?php

declare(strict_types=1);

namespace App\Tests\Functional\Module\Core;

use App\Module\Core\Domain\Entity\User;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Uid\Uuid;

/**
 * @internal
 */
final class AuditLogApiTest extends WebTestCase
{
    public function testGetCollectionRequiresAuthentication(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();

        $client->request('GET', '/api/audit-logs');

        self::assertResponseStatusCodeSame(401);
    }

    public function testUserWithoutPermissionIsForbidden(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();
        $this->loginUser($client, 'alice');

        $client->request('GET', '/api/audit-logs');

        self::assertResponseStatusCodeSame(403);
    }

    public function testAdminCanListAuditLogs(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();
        $this->loginUser($client, 'admin');

        $client->request('GET', '/api/audit-logs');

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertArrayHasKey('member', $data);
        self::assertArrayHasKey('totalItems', $data);
        self::assertGreaterThanOrEqual(3, $data['totalItems']);
    }

    public function testFilterByActionReturnsOnlyMatchingEntries(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();
        $this->loginUser($client, 'admin');

        $client->request('GET', '/api/audit-logs?action=update');

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertNotEmpty($data['member']);
        foreach ($data['member'] as $entry) {
            self::assertSame('update', $entry['action']);
        }
    }

    public function testFilterByEntityType(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();
        $this->loginUser($client, 'admin');

        $client->request('GET', '/api/audit-logs?entity_type=core.User');

        self::assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        self::assertNotEmpty($data['member']);
        foreach ($data['member'] as $entry) {
            self::assertSame('core.User', $entry['entityType']);
        }
    }

    public function testInvalidActionFilterIsRejected(): void
    {
        $client = self::createClient();
        $this->seedAuditLog();
        $this->loginUser($client, 'admin');

        $client->request('GET', '/api/audit-logs?action=bogus');

        self::assertResponseStatusCodeSame(400);
    }

    /**
     * Insert deterministic audit rows directly through the audit connection.
     *
     * The table audit_log has no ORM entity (written via raw DBAL), so we
     * clean and seed it via the dedicated connection. These tests are not
     * wrapped in a rolled-back transaction, hence the upfront DELETE.
     */
    private function seedAuditLog(): void
    {
        /** @var Connection $audit */
        $audit = self::getContainer()->get('doctrine.dbal.audit_connection');
        $audit->executeStatement('DELETE FROM audit_log');

        $rows = [
            ['core.User', 'create', '{"username":"seed-create"}'],
            ['core.User', 'update', '{"firstName":{"old":"a","new":"b"}}'],
            ['core.Role', 'delete', '{}'],
        ];

        $performedAt = '2026-04-22 10:00:00';
        foreach ($rows as $i => [$entityType, $action, $changes]) {
            $audit->insert('audit_log', [
                'id'           => Uuid::v7()->toRfc4122(),
                'entity_type'  => $entityType,
                'entity_id'    => (string) ($i + 1),
                'action'       => $action,
                'changes'      => $changes,
                'performed_by' => 'admin',
                'performed_at' => $performedAt,
                'ip_address'   => '127.0.0.1',
                'request_id'   => 'req-'.$i,
            ]);
        }
    }

    private function loginUser(KernelBrowser $client, string $username): void
    {
        $em   = self::getContainer()->get(EntityManagerInterface::class);
        $user = $em->getRepository(User::class)->findOneBy(['username' => $username]);
        self::assertInstanceOf(User::class, $user);
        $client->loginUser($user);
    }
}
