<?php

namespace App\Tests;

use App\Auth\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

/**
 * Base class for functional API tests.
 *
 * Provides a booted KernelBrowser, the EntityManager, and helpers to create
 * users and issue JWTs so tests can hit authenticated /api and /oauth endpoints.
 * Runs against the dedicated db_test database (see .env.test / doctrine when@test).
 */
abstract class ApiTestCase extends WebTestCase
{
    protected KernelBrowser $client;
    protected EntityManagerInterface $em;

    protected function setUp(): void
    {
        $this->client = static::createClient();
        $this->em = static::getContainer()->get(EntityManagerInterface::class);
    }

    /**
     * Persist a user with the given roles. Mobile is randomised per test to
     * avoid unique-constraint clashes across cases without a full DB reset.
     */
    protected function createUser(array $roles = ['ROLE_USER'], ?string $mobile = null): User
    {
        // 9 random digits after 09 (full ^09\d{9}$ space) — db_test is never reset,
        // so a narrower space eventually collides on the unique mobile.
        $mobile ??= '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
        $user = new User($mobile);
        $user->setRoles($roles);
        $user->setStatus(1);
        $this->em->persist($user);
        $this->em->flush();

        return $user;
    }

    protected function jwtFor(User $user): string
    {
        return static::getContainer()
            ->get(JWTTokenManagerInterface::class)
            ->create($user);
    }

    /**
     * Issue an authenticated JSON request and return the decoded response body.
     */
    protected function authJson(string $method, string $uri, User $user, array $body = []): array
    {
        $this->client->request(
            $method,
            $uri,
            server: [
                'HTTP_AUTHORIZATION' => 'Bearer ' . $this->jwtFor($user),
                'CONTENT_TYPE'       => 'application/json',
            ],
            content: $body ? json_encode($body) : null,
        );

        return json_decode($this->client->getResponse()->getContent(), true) ?? [];
    }

    protected function responseCode(): int
    {
        return $this->client->getResponse()->getStatusCode();
    }

    /**
     * Count the SQL queries executed while running $fn. Used to assert that a
     * list endpoint's query count does not grow with the number of rows (N+1).
     */
    protected function countQueries(callable $fn): int
    {
        $holder = static::getContainer()->get('doctrine.debug_data_holder');
        $holder->reset();
        $fn();

        return array_sum(array_map('count', $holder->getData()));
    }
}
