<?php

/**
 * 文件功能：前台认证控制器功能测试
 *
 * 覆盖登录即注册、老密码升级、封禁限制、退出登录、
 * session 轮换与服务端限流等关键认证行为。
 */

namespace Tests\Feature;

use App\Models\Sysparam;
use App\Models\User;
use App\Models\UsernameBlacklist;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Validator;
use Tests\TestCase;

/**
 * 类功能：验证前台认证链路的核心行为。
 */
class AuthControllerTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 初始化验证码桩与基础系统参数。
     */
    protected function setUp(): void
    {
        parent::setUp();

        // Mock captcha validation to always pass
        Validator::extend('captcha', function () {
            return true;
        });

        // Mock Sysparam to return a default superlevel
        Sysparam::updateOrCreate(
            ['alias' => 'superlevel'],
            ['body' => '100']
        );
    }

    /**
     * 测试普通登录成功后会轮换 session id，阻断会话固定风险。
     */
    public function test_login_regenerates_session_id(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);

        $user = User::factory()->create([
            'username' => 'session-user',
            'password' => Hash::make('password123'),
        ]);

        $this->startSession();
        $oldSessionId = session()->getId();

        $response = $this->postJson('/login', [
            'username' => 'session-user',
            'password' => 'password123',
            'captcha' => '1234',
        ]);

        $response->assertOk()
            ->assertJsonPath('status', 'success');
        $this->assertAuthenticatedAs($user);
        $this->assertNotSame($oldSessionId, session()->getId());
    }

    /**
     * 验证首次登录的用户会被自动注册并完成登录。
     */
    public function test_can_register_new_user(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);

        $response = $this->postJson('/login', [
            'username' => 'newuser',
            'password' => 'secret123',
            'captcha' => '1234',
            'bSex' => '1',
        ]);

        $response->assertStatus(200)
            ->assertJsonPath('status', 'success');

        $this->assertDatabaseHas('users', [
            'username' => 'newuser',
            'user_level' => 1,
            'sex' => 1,
        ]);

        $this->assertAuthenticated();
    }

    /**
     * 验证命中黑名单的用户名会被拒绝注册。
     */
    public function test_cannot_register_with_blacklisted_username(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);

        UsernameBlacklist::create([
            'username' => 'admin',
            'type' => 'permanent',
        ]);

        $response = $this->postJson('/login', [
            'username' => 'admin',
            'password' => 'secret123',
            'captcha' => '1234',
        ]);

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

        $this->assertDatabaseMissing('users', [
            'username' => 'admin',
        ]);

        $this->assertGuest();
    }

    /**
     * 验证已有用户可以通过正确密码登录。
     */
    public function test_can_login_existing_user(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);

        $user = User::factory()->create([
            'username' => 'testuser',
            'password' => Hash::make('password123'),
        ]);

        $response = $this->postJson('/login', [
            'username' => 'testuser',
            'password' => 'password123',
            'captcha' => '1234',
        ]);

        $response->assertStatus(200)
            ->assertJsonPath('status', 'success');

        $this->assertAuthenticatedAs($user);
    }

    /**
     * 验证旧版 MD5 密码会在成功登录后升级为 bcrypt。
     */
    public function test_login_md5_user_upgrades_to_bcrypt(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);

        $password = 'oldsecret';
        $user = User::factory()->create([
            'username' => 'olduser',
            'password' => 'temp',
        ]);

        \Illuminate\Support\Facades\DB::table('users')
            ->where('id', $user->id)
            ->update(['password' => md5($password)]);

        $response = $this->postJson('/login', [
            'username' => 'olduser',
            'password' => $password,
            'captcha' => '1234',
        ]);

        if ($response->status() !== 200) {
            dd($response->json());
        }

        $response->assertStatus(200)
            ->assertJsonPath('status', 'success');

        $user->refresh();
        $this->assertTrue(Hash::check($password, $user->password));
        $this->assertAuthenticatedAs($user);
    }

    /**
     * 验证被封禁账号无法登录。
     */
    public function test_banned_user_cannot_login(): void
    {
        $user = User::factory()->create([
            'username' => 'banneduser',
            'password' => Hash::make('secret123'),
            'user_level' => -1, // banned
        ]);

        $response = $this->postJson('/login', [
            'username' => 'banneduser',
            'password' => 'secret123',
            'captcha' => '1234',
        ]);

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

        $this->assertGuest();
    }

    /**
     * 验证被封禁 IP 无法登录普通账号。
     */
    public function test_banned_ip_cannot_login(): void
    {
        Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(true);

        $user = User::factory()->create([
            'username' => 'normaluser',
            'password' => Hash::make('secret'),
        ]);

        $response = $this->postJson('/login', [
            'username' => 'normaluser',
            'password' => 'secret',
            'captcha' => '1234',
        ]);

        $response->assertStatus(403);
        $this->assertGuest();
    }

    /**
     * 验证已登录用户可以正常退出。
     */
    public function test_can_logout(): void
    {
        /** @var \App\Models\User $user */
        $user = User::factory()->create();

        $response = $this->actingAs($user)->post('/logout');

        $response->assertRedirect('/');
        $this->assertGuest();
    }

    /**
     * 测试前台登录接口在连续失败后会触发服务端限流。
     */
    public function test_login_route_is_rate_limited_after_repeated_failures(): void
    {
        RateLimiter::clear('chat-login|rate-user|127.0.0.1');
        Redis::shouldReceive('sismember')->zeroOrMoreTimes()->andReturn(false);

        User::factory()->create([
            'username' => 'rate-user',
            'password' => Hash::make('correct-password'),
        ]);

        for ($attempt = 1; $attempt <= 5; $attempt++) {
            $response = $this->withServerVariables(['REMOTE_ADDR' => '127.0.0.1'])
                ->postJson('/login', [
                    'username' => 'rate-user',
                    'password' => 'wrong-password',
                    'captcha' => '1234',
                ]);

            $response->assertStatus(422);
        }

        $rateLimitedResponse = $this->withServerVariables(['REMOTE_ADDR' => '127.0.0.1'])
            ->postJson('/login', [
                'username' => 'rate-user',
                'password' => 'wrong-password',
                'captcha' => '1234',
            ]);

        $rateLimitedResponse->assertStatus(429)
            ->assertJsonPath('status', 'error');
    }
}
