<?php

namespace Tests\Feature;

use App\Models\GameConfig;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Redis;
use Tests\TestCase;

/**
 * 类功能：覆盖钓鱼小游戏抛竿、收竿和令牌校验流程。
 */
class FishingControllerTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 初始化钓鱼配置并清理 Redis 状态。
     */
    protected function setUp(): void
    {
        parent::setUp();
        Redis::flushall();

        GameConfig::updateOrCreate(
            ['game_key' => 'fishing'],
            [
                'name' => 'Fishing',
                'icon' => 'fish',
                'description' => 'Fishing Game',
                'enabled' => true,
                'params' => [
                    'fishing_cost' => 5,
                    'fishing_cooldown' => 300,
                ],
            ]
        );
    }

    /**
     * 方法功能：验证用户金币足够时可以正常抛竿并扣除费用。
     */
    public function test_can_cast_rod(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 10]);

        $response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));

        $response->assertStatus(200);
        $response->assertJson([
            'status' => 'success',
            'cost' => 5,
        ]);

        $this->assertEquals(5, $user->fresh()->jjb);
    }

    /**
     * 方法功能：验证冷却期间不能重复抛竿。
     */
    public function test_cannot_cast_when_on_cooldown(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 10]);
        Redis::setex("fishing:cd:{$user->id}", 100, time());

        $response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));

        $response->assertStatus(429);
        $response->assertJson([
            'status' => 'error',
        ]);
    }

    /**
     * 方法功能：验证金币不足时不能抛竿。
     */
    public function test_cannot_cast_without_enough_gold(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 2]);

        $response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));

        $response->assertStatus(422);
        $response->assertJson([
            'status' => 'error',
        ]);
    }

    /**
     * 方法功能：验证等待完成后携带正确令牌可以成功收竿。
     */
    public function test_can_reel_after_waiting(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 10]);

        $token = 'test-token';

        Redis::set("fishing:token:{$user->id}", json_encode([
            'token' => $token,
            'cast_at' => time() - 1, // Simulate past
            'wait_time' => 0,
        ]));

        $response = $this->actingAs($user)->postJson(route('fishing.reel', ['id' => 1]), [
            'token' => $token,
        ]);

        $response->assertStatus(200);
        $response->assertJson([
            'status' => 'success',
        ]);

        // Cooldown should be set
        $this->assertTrue((bool) Redis::exists("fishing:cd:{$user->id}"));
    }

    /**
     * 方法功能：验证错误令牌不能收竿。
     */
    public function test_cannot_reel_with_invalid_token(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 10]);

        Redis::set("fishing:token:{$user->id}", json_encode([
            'token' => 'valid-token',
            'cast_at' => time(),
            'wait_time' => 0,
        ]));

        $response = $this->actingAs($user)->postJson(route('fishing.reel', ['id' => 1]), [
            'token' => 'invalid-token',
        ]);

        $response->assertStatus(422);
        $response->assertJson([
            'status' => 'error',
        ]);
    }

    /**
     * 方法功能：验证已有未收竿令牌时再次抛竿会恢复原会话且不会重复扣金币。
     */
    public function test_cast_restores_active_token_without_charging_again(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create(['jjb' => 10]);
        $tokenKey = "fishing:token:{$user->id}";
        $payload = json_encode([
            'token' => 'active-token',
            'cast_at' => time(),
            'wait_time' => 10,
        ]);

        Redis::setex($tokenKey, 20, $payload);

        $response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));

        $response->assertStatus(200);
        $response->assertJson([
            'status' => 'success',
            'token' => 'active-token',
            'cost' => 0,
            'restored' => true,
        ]);

        $this->assertSame(10, $user->fresh()->jjb);
        $this->assertSame($payload, Redis::get($tokenKey));
    }
}
