<?php

namespace Tests\Feature;

use App\Enums\AccessRequestStatus;
use App\Models\AccessRequests;
use App\Models\GameServers;
use App\Models\User;
use App\Services\AccessRequestApprover;
use App\Services\MinecraftRconClient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use RuntimeException;
use Tests\TestCase;

class AccessRequestApproverTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_accepts_an_access_request_after_running_rcon_add(): void
    {
        config()->set('rcon.access_command', 'whitelist add {username}');

        $rconClient = new FakeMinecraftRconClient;
        $this->app->instance(MinecraftRconClient::class, $rconClient);
        $reviewer = User::factory()->create();
        $accessRequest = $this->pendingAccessRequest('LeonPlayer');

        $output = app(AccessRequestApprover::class)->approve($accessRequest, $reviewer);

        $this->assertSame('Added LeonPlayer to the whitelist', $output);
        $this->assertSame(['whitelist add LeonPlayer'], $rconClient->commands);

        $this->assertDatabaseHas('access_requests', [
            'id' => $accessRequest->id,
            'status' => AccessRequestStatus::ACCEPTED->value,
            'reviewed_by' => $reviewer->id,
        ]);

        $this->assertNotNull($accessRequest->fresh()->reviewed_at);
    }

    public function test_it_does_not_accept_when_rcon_fails(): void
    {
        $this->app->instance(MinecraftRconClient::class, new FakeMinecraftRconClient(shouldFail: true));
        $accessRequest = $this->pendingAccessRequest('LeonPlayer');

        $this->expectException(RuntimeException::class);

        try {
            app(AccessRequestApprover::class)->approve($accessRequest);
        } finally {
            $this->assertDatabaseHas('access_requests', [
                'id' => $accessRequest->id,
                'status' => AccessRequestStatus::PENDING->value,
                'reviewed_by' => null,
                'reviewed_at' => null,
            ]);
        }
    }

    private function pendingAccessRequest(string $gameUsername): AccessRequests
    {
        $server = GameServers::create([
            'name' => 'Survival',
            'slug' => 'survival',
        ]);

        return AccessRequests::create([
            'status' => AccessRequestStatus::PENDING,
            'game_username' => $gameUsername,
            'message' => '',
            'server_id' => $server->id,
        ]);
    }
}

class FakeMinecraftRconClient extends MinecraftRconClient
{
    /**
     * @var array<int, string>
     */
    public array $commands = [];

    public function __construct(
        private readonly bool $shouldFail = false,
    ) {}

    public function command(string $command): string
    {
        $this->commands[] = $command;

        if ($this->shouldFail) {
            throw new RuntimeException('rcon failed');
        }

        return 'Added LeonPlayer to the whitelist';
    }
}
