<?php

/**
 * 文件功能：聊天室座驾接口功能测试。
 *
 * 覆盖座驾列表、购买、续期、替换、余额不足和后台配置保存。
 */

namespace Tests\Feature;

use App\Enums\CurrencySource;
use App\Models\Ride;
use App\Models\Room;
use App\Models\User;
use App\Models\UserRidePurchase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Redis;
use Tests\TestCase;

/**
 * 座驾控制器功能测试
 * 验证前台座驾购买接口与后台商品配置的核心行为。
 */
class RideControllerTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 每个测试前清空 Redis，避免房间在线状态串扰。
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->flushChatRoomRedisState();
    }

    /**
     * 测试座驾列表返回上架座驾、当前座驾和购买记录。
     */
    public function test_items_returns_ride_items_and_purchase_state(): void
    {
        $user = User::factory()->create();
        $ride = $this->createRide(['name' => '歼-35测试座驾', 'slug' => 'ride_j35']);

        UserRidePurchase::create([
            'user_id' => $user->id,
            'ride_id' => $ride->id,
            'status' => 'active',
            'price_paid' => 18888,
            'expires_at' => now()->addDays(3),
        ]);

        $response = $this->actingAs($user)->getJson(route('rides.items'));

        $response->assertOk()
            ->assertJsonFragment(['slug' => 'ride_j35'])
            ->assertJsonPath('current_ride.item.slug', 'ride_j35')
            ->assertJsonPath('purchases.0.item.slug', 'ride_j35');
    }

    /**
     * 测试金币不足时不能购买座驾。
     */
    public function test_cannot_buy_ride_when_balance_is_not_enough(): void
    {
        $user = User::factory()->create(['jjb' => 100]);
        $room = Room::create(['room_name' => '座驾余额房']);
        $this->joinRoom($user, $room);
        $ride = $this->createRide(['price' => 18888]);

        $response = $this->actingAs($user)->postJson(route('rides.buy'), [
            'item_id' => $ride->id,
            'room_id' => $room->id,
        ]);

        $response->assertStatus(400)
            ->assertJsonPath('status', 'error');

        $this->assertSame(100, (int) $user->fresh()->jjb);
        $this->assertDatabaseMissing('user_ride_purchases', [
            'user_id' => $user->id,
            'ride_id' => $ride->id,
        ]);
        $this->assertDatabaseMissing('user_currency_logs', [
            'user_id' => $user->id,
            'source' => CurrencySource::RIDE_BUY->value,
        ]);
    }

    /**
     * 测试购买座驾会扣金币并生成当前激活记录。
     */
    public function test_can_buy_ride_and_activate_it(): void
    {
        $user = User::factory()->create(['jjb' => 30000]);
        $room = Room::create(['room_name' => '座驾购买房']);
        $this->joinRoom($user, $room);
        $ride = $this->createRide(['slug' => 'ride_99a', 'price' => 18888, 'duration_days' => 7]);

        $response = $this->actingAs($user)->postJson(route('rides.buy'), [
            'item_id' => $ride->id,
            'room_id' => $room->id,
        ]);

        $response->assertOk()
            ->assertJsonPath('status', 'success')
            ->assertJsonPath('current_ride.item.slug', 'ride_99a')
            ->assertJsonPath('jjb', 11112);

        $this->assertDatabaseHas('user_ride_purchases', [
            'user_id' => $user->id,
            'ride_id' => $ride->id,
            'status' => 'active',
            'price_paid' => 18888,
        ]);
        $this->assertDatabaseHas('user_currency_logs', [
            'user_id' => $user->id,
            'currency' => 'gold',
            'amount' => -18888,
            'balance_after' => 11112,
            'source' => CurrencySource::RIDE_BUY->value,
            'remark' => '购买聊天室座驾：测试座驾',
            'room_id' => $room->id,
        ]);

        $messages = app(\App\Services\ChatStateService::class)->getNewMessages($room->id, 0);
        $purchaseNotice = collect($messages)->first(fn (array $message): bool => ($message['action'] ?? '') === 'ride_purchase');

        $this->assertNotNull($purchaseNotice);
        $this->assertSame('系统传音', $purchaseNotice['from_user']);
        $this->assertStringContainsString('【座驾】', $purchaseNotice['content']);
        $this->assertStringContainsString($user->username, $purchaseNotice['content']);
        $this->assertStringContainsString('测试座驾', $purchaseNotice['content']);
        $this->assertStringContainsString('openRideModal()', $purchaseNotice['content']);
        $this->assertStringContainsString('购买座驾', $purchaseNotice['content']);
    }

    /**
     * 测试同款座驾续购会叠加有效期且保持一条 active 当前座驾。
     */
    public function test_buying_same_ride_extends_active_purchase(): void
    {
        $user = User::factory()->create(['jjb' => 50000]);
        $room = Room::create(['room_name' => '座驾续费房']);
        $this->joinRoom($user, $room);
        $ride = $this->createRide(['price' => 1000, 'duration_days' => 7]);

        $purchase = UserRidePurchase::create([
            'user_id' => $user->id,
            'ride_id' => $ride->id,
            'status' => 'active',
            'price_paid' => 1000,
            'expires_at' => now()->addDays(2),
        ]);

        $response = $this->actingAs($user)->postJson(route('rides.buy'), [
            'item_id' => $ride->id,
            'room_id' => $room->id,
        ]);

        $response->assertOk();

        $this->assertSame('cancelled', $purchase->fresh()->status);
        $activePurchase = UserRidePurchase::query()
            ->where('user_id', $user->id)
            ->where('ride_id', $ride->id)
            ->where('status', 'active')
            ->firstOrFail();
        $this->assertSame(1000, (int) $activePurchase->price_paid);
        $this->assertTrue($activePurchase->expires_at->greaterThan(now()->addDays(8)));
        $this->assertSame(1, UserRidePurchase::query()->where('user_id', $user->id)->where('status', 'active')->count());
    }

    /**
     * 测试购买不同座驾会取消旧座驾并激活新座驾。
     */
    public function test_buying_different_ride_cancels_previous_active_ride(): void
    {
        $user = User::factory()->create(['jjb' => 50000]);
        $room = Room::create(['room_name' => '座驾替换房']);
        $this->joinRoom($user, $room);
        $oldRide = $this->createRide(['slug' => 'ride_j35', 'price' => 1000]);
        $newRide = $this->createRide(['slug' => 'ride_df5c', 'price' => 2000]);

        $oldPurchase = UserRidePurchase::create([
            'user_id' => $user->id,
            'ride_id' => $oldRide->id,
            'status' => 'active',
            'price_paid' => 1000,
            'expires_at' => now()->addDays(3),
        ]);

        $response = $this->actingAs($user)->postJson(route('rides.buy'), [
            'item_id' => $newRide->id,
            'room_id' => $room->id,
        ]);

        $response->assertOk()
            ->assertJsonPath('current_ride.item.slug', 'ride_df5c');

        $this->assertSame('cancelled', $oldPurchase->fresh()->status);
        $this->assertDatabaseHas('user_ride_purchases', [
            'user_id' => $user->id,
            'ride_id' => $newRide->id,
            'status' => 'active',
        ]);
    }

    /**
     * 测试后台座驾独立模块可以保存欢迎语字段。
     */
    public function test_admin_can_store_ride_with_welcome_message(): void
    {
        $admin = User::factory()->create(['id' => 1, 'user_level' => 100]);

        $response = $this->actingAs($admin)->post(route('admin.rides.store'), [
            'name' => '测试座驾',
            'slug' => 'ride_test',
            'effect_key' => 'test',
            'icon' => '🚘',
            'description' => '测试座驾说明',
            'price' => 12345,
            'duration_days' => 7,
            'welcome_message' => '【{name}】驾驶【{ride}】入场',
            'sort_order' => 99,
            'is_active' => 1,
        ]);

        $response->assertRedirect(route('admin.rides.index'));
        $this->assertDatabaseHas('rides', [
            'slug' => 'ride_test',
            'effect_key' => 'test',
            'welcome_message' => '【{name}】驾驶【{ride}】入场',
        ]);
    }

    /**
     * 测试后台座驾管理页面可以显示独立座驾数据。
     */
    public function test_admin_can_view_ride_management_page(): void
    {
        $admin = User::factory()->create(['id' => 1, 'user_level' => 100]);
        $ride = $this->createRide(['name' => '后台可见座驾', 'slug' => 'ride_admin_visible']);

        $response = $this->actingAs($admin)->get(route('admin.rides.index'));

        $response->assertOk()
            ->assertSee('座驾管理')
            ->assertSee($ride->name)
            ->assertSee('ride_admin_visible');
    }

    /**
     * 创建测试用独立座驾。
     *
     * @param  array<string, mixed>  $attributes  覆盖字段
     */
    private function createRide(array $attributes = []): Ride
    {
        $data = array_merge([
            'name' => '测试座驾',
            'slug' => 'ride_test_'.str()->random(8),
            'effect_key' => 'test_'.str()->random(8),
            'description' => '测试座驾说明',
            'icon' => '🚘',
            'price' => 1000,
            'duration_days' => 7,
            'sort_order' => 80,
            'is_active' => true,
            'welcome_message' => '【{name}】驾驶【{ride}】入场',
        ], $attributes);

        return Ride::query()->updateOrCreate(
            ['slug' => $data['slug']],
            $data,
        );
    }

    /**
     * 将测试用户写入指定房间在线表，模拟已经进入聊天室的状态。
     */
    private function joinRoom(User $user, Room $room): void
    {
        Redis::hset("room:{$room->id}:users", $user->username, json_encode([
            'id' => $user->id,
            'username' => $user->username,
        ], JSON_UNESCAPED_UNICODE));
    }
}
