<?php declare(strict_types=1);
/**
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2026 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace App\Tests\Service;

use App\Service\MercureJwtService;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

/**
 * Class MercureJwtServiceTest
 *
 * @package  App\Tests\Service
 * @author   Lang <https://www.splendidbear.org>
 * @category Class
 * @license  https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
 * @link     www.splendidbear.org
 * @since    2026. 04. 21.
 */
#[TestDox('Mercure Jwt Service')]
class MercureJwtServiceTest extends TestCase
{
    /** JWT HS256 requires at least 32 characters for the secret key  */
    private const SECRET = 'test-mercure-secret-key-12345678901234567890';

    private MercureJwtService $service;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = new MercureJwtService(self::SECRET);
    }

    #[Test]
    #[TestDox('Mint subscriber token returns valid jwt')]
    public function mintSubscriberTokenReturnsValidJwt(): void
    {
        $token = $this->service->mintSubscriberToken('game123', 'player1');

        $this->assertIsString($token);
        $this->assertNotEmpty($token);

        /** Token should have 3 parts (header.payload.signature)  */
        $parts = explode('.', $token);
        $this->assertCount(3, $parts);
    }

    #[Test]
    #[TestDox('Mint subscriber token with different game assoc')]
    public function mintSubscriberTokenWithDifferentGameAssoc(): void
    {
        $token1 = $this->service->mintSubscriberToken('gameA', 'player1');
        $token2 = $this->service->mintSubscriberToken('gameB', 'player1');

        /** Different gameAssoc should result in different tokens  */
        $this->assertNotSame($token1, $token2);
    }

    #[Test]
    #[TestDox('Mint subscriber token with different user names')]
    public function mintSubscriberTokenWithDifferentUserNames(): void
    {
        $token1 = $this->service->mintSubscriberToken('game1', 'playerA');
        $token2 = $this->service->mintSubscriberToken('game1', 'playerB');

        /** Different usernames should result in different tokens  */
        $this->assertNotSame($token1, $token2);
    }

    #[Test]
    #[TestDox('Mint subscriber token contains proper structure')]
    public function mintSubscriberTokenContainsProperStructure(): void
    {
        $token = $this->service->mintSubscriberToken('game123', 'testplayer');

        /** Decode without verification to check structure  */
        $parts = explode('.', $token);

        /** Decode payload (middle part)  */
        $payload = json_decode(base64_decode($parts[1] . str_repeat('=', (4 - strlen($parts[1]) % 4))), true);

        $this->assertIsArray($payload);
        $this->assertArrayHasKey('mercure', $payload);
        $this->assertArrayHasKey('subscribe', $payload['mercure']);
        $this->assertArrayHasKey('payload', $payload['mercure']);
    }

    #[Test]
    #[TestDox('Mint subscriber token payload contains correct data')]
    public function mintSubscriberTokenPayloadContainsCorrectData(): void
    {
        $token = $this->service->mintSubscriberToken('test-game', 'test-user');

        $parts = explode('.', $token);
        $payload = json_decode(base64_decode($parts[1] . str_repeat('=', (4 - strlen($parts[1]) % 4))), true);

        $this->assertSame('test-user', $payload['mercure']['payload']['username']);
        $this->assertSame('test-game', $payload['mercure']['payload']['gameAssoc']);
        $this->assertContains('*', $payload['mercure']['subscribe']);
    }
}
