<?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\Integration;

use App\Entity\PlayedGame;
use App\Entity\User;
use App\Tests\Factory\ContactMessageFactory;
use App\Tests\Factory\GamerFactory;
use App\Tests\Factory\GridFactory;
use App\Tests\Factory\PlayedGameFactory;
use App\Tests\Factory\StepFactory;
use App\Tests\Factory\UserFactory;
use App\Tests\Factory\WebAuthnCredentialFactory;
use App\Tests\WebTestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestDox;

/**
 * Class FactoryExampleTest
 *
 * Example test demonstrating Foundry factory usage
 *
 * @package  App\Tests\Integration
 * @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('Factory Examples')]
class FactoryExampleTest extends WebTestCase
{
    #[Test]
    #[TestDox('Creates a verified user with UserFactory')]
    public function createUser(): void
    {
        $user = UserFactory::createOne();

        self::assertInstanceOf(User::class, $user->_real());
        self::assertNotNull($user->username);
        self::assertNotNull($user->email);
        self::assertTrue($user->isVerified);
    }

    #[Test]
    #[TestDox('Creates an unverified user')]
    public function createUnverifiedUser(): void
    {
        $user = UserFactory::createOne(['isVerified' => false]);

        self::assertFalse($user->isVerified);
    }

    #[Test]
    #[TestDox('Creates multiple users at once')]
    public function createMultipleUsers(): void
    {
        UserFactory::createMany(5);

        self::assertCount(5, UserFactory::repository()->findAll());
    }

    #[Test]
    #[TestDox('Creates an anonymous gamer with guest username')]
    public function createAnonymousGamer(): void
    {
        $gamer = GamerFactory::new()->anonymous()->create();

        self::assertStringStartsWith('Guest_', $gamer->userName);
        self::assertNotNull($gamer->ip);
        self::assertNotNull($gamer->connTimestamp);
    }

    #[Test]
    #[TestDox('Creates a game with two registered players')]
    public function createGameWithRegisteredPlayers(): void
    {
        $game = PlayedGameFactory::new()
            ->withRegisteredPlayers()
            ->create();

        self::assertInstanceOf(PlayedGame::class, $game->_real());
        self::assertNotNull($game->red);
        self::assertNotNull($game->blue);
        self::assertNull($game->redAnon);
        self::assertNull($game->blueAnon);
    }

    #[Test]
    #[TestDox('Creates a game with two anonymous players')]
    public function createGameWithAnonymousPlayers(): void
    {
        $game = PlayedGameFactory::new()
            ->withAnonymousPlayers()
            ->create();

        self::assertNull($game->red);
        self::assertNull($game->blue);
        self::assertNotNull($game->redAnon);
        self::assertNotNull($game->blueAnon);
    }

    #[Test]
    #[TestDox('Creates a finished game where red player wins')]
    public function createFinishedGame(): void
    {
        $game = PlayedGameFactory::new()
            ->withRegisteredPlayers()
            ->redWins()
            ->create();

        self::assertEquals(26, $game->redPoints);
        self::assertLessThan(26, $game->bluePoints);
    }

    #[Test]
    #[TestDox('Creates a game with multiple steps from both players')]
    public function createGameWithSteps(): void
    {
        $game = PlayedGameFactory::new()
            ->withRegisteredPlayers()
            ->create();

        StepFactory::new()
            ->forPlayer('red')
            ->mine()
            ->create(['playedGame' => $game]);

        StepFactory::new()
            ->forPlayer('red')
            ->mine()
            ->create(['playedGame' => $game]);

        StepFactory::new()
            ->forPlayer('red')
            ->mine()
            ->create(['playedGame' => $game]);

        StepFactory::new()
            ->forPlayer('blue')
            ->safe()
            ->create(['playedGame' => $game]);

        StepFactory::new()
            ->forPlayer('blue')
            ->safe()
            ->create(['playedGame' => $game]);

        self::assertCount(5, StepFactory::repository()->findAll());
    }

    #[Test]
    #[TestDox('Creates a game with a 16x16 grid')]
    public function createGameWithGrid(): void
    {
        $game = PlayedGameFactory::new()
            ->withRegisteredPlayers()
            ->create();

        $grid = GridFactory::createOne(['playedGame' => $game]);

        self::assertCount(16, $grid->gridRow);
    }

    #[Test]
    #[TestDox('Creates a WebAuthn credential for a user')]
    public function createWebAuthnCredential(): void
    {
        $user = UserFactory::createOne();

        $credential = WebAuthnCredentialFactory::new()
            ->withName('YubiKey 5C')
            ->recentlyUsed()
            ->create(['user' => $user]);

        self::assertEquals($user->_real(), $credential->user);
        self::assertEquals('YubiKey 5C', $credential->credentialName);
        self::assertNotNull($credential->lastUsedAt);
    }

    #[Test]
    #[TestDox('Creates a contact message with consent')]
    public function createContactMessage(): void
    {
        $message = ContactMessageFactory::createOne();

        self::assertNotNull($message->name);
        self::assertNotNull($message->email);
        self::assertNotNull($message->content);
        self::assertTrue($message->consent);
        self::assertNotNull($message->createdAt);
    }

    #[Test]
    #[TestDox('Tests are isolated - database is reset between tests')]
    public function databaseIsolation(): void
    {
        UserFactory::createMany(3);
        self::assertCount(3, UserFactory::repository()->findAll());

        /** Database will be reset before next test due to ResetDatabase trait */
    }
}
