<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Integration\Auth;

use App\Model\ApiKey;
use App\Model\User;
use HyperfTest\TestCase;
use Qbhy\HyperfAuth\AuthManager;

use function Hyperf\Support\make;

/**
 * API Key 认证流程 & CRUD 集成测试
 *
 * @internal
 * @coversNothing
 */
class ApiKeyAuthTest extends TestCase
{
    protected function getAuthToken(User $user): string
    {
        $auth = make(AuthManager::class);
        return $auth->guard('jwt')->login($user);
    }

    protected function authHeaders(User $user): array
    {
        return ['Authorization' => 'Bearer ' . $this->getAuthToken($user)];
    }

    protected function createTestUser(array $overrides = []): User
    {
        $suffix = bin2hex(random_bytes(4));

        return User::query()->create(array_merge([
            'username' => 'apikey_int_' . $suffix,
            'password' => 'Pass_' . $suffix,
            'email' => 'apikey_int_' . $suffix . '@example.com',
            'status' => 1,
            'api_key_enabled' => true,
        ], $overrides));
    }

    // ========== API Key CRUD ==========

    public function test_generate_api_key_success(): void
    {
        $user = $this->createTestUser();

        $response = $this->post('/api/v1/me/api-keys', [
            'name' => 'IntTest Key',
        ], $this->authHeaders($user));

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
        $response->assertJsonStructure([
            'data' => ['plain_key', 'api_key' => ['id', 'name', 'key_prefix', 'enabled']],
        ]);
    }

    public function test_generate_api_key_without_api_key_enabled_returns_403(): void
    {
        $user = $this->createTestUser(['api_key_enabled' => false]);

        $response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Blocked Key',
        ], $this->authHeaders($user));

        $response->assertStatus(403);
        $response->assertJsonPath('code', 403);
    }

    public function test_generate_api_key_missing_name_returns_400(): void
    {
        $user = $this->createTestUser();

        $response = $this->post('/api/v1/me/api-keys', [], $this->authHeaders($user));

        $response->assertStatus(400);
        $response->assertJsonPath('code', 400);
    }

    public function test_generate_api_key_without_auth_returns_401(): void
    {
        $response = $this->post('/api/v1/me/api-keys', [
            'name' => 'No Auth Key',
        ]);

        $response->assertStatus(401);
    }

    public function test_list_api_keys(): void
    {
        $user = $this->createTestUser();

        // 先创建一个 key
        $this->post('/api/v1/me/api-keys', [
            'name' => 'List Test Key',
        ], $this->authHeaders($user));

        $response = $this->get('/api/v1/me/api-keys', [], $this->authHeaders($user));

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
    }

    public function test_delete_api_key_success(): void
    {
        $user = $this->createTestUser();

        // 创建一个 key
        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Delete Test Key',
        ], $this->authHeaders($user));

        $key_id = $create_response->json('data.api_key.id');

        $response = $this->delete('/api/v1/me/api-keys/' . $key_id, [], $this->authHeaders($user));

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
    }

    public function test_delete_other_users_api_key_returns_404(): void
    {
        $user_a = $this->createTestUser();
        $user_b = $this->createTestUser();

        // user_a 创建 key
        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'UserA Key',
        ], $this->authHeaders($user_a));

        $key_id = $create_response->json('data.api_key.id');

        // user_b 尝试删除
        $response = $this->delete('/api/v1/me/api-keys/' . $key_id, [], $this->authHeaders($user_b));

        $response->assertStatus(404);
        $response->assertJsonPath('code', 404);
    }

    // ========== API Key 认证 ==========

    public function test_api_key_auth_access_me_endpoint(): void
    {
        $user = $this->createTestUser();

        // 通过 JWT 创建 API Key
        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Auth Test Key',
        ], $this->authHeaders($user));

        $plain_key = $create_response->json('data.plain_key');

        // 使用 API Key 访问 /me
        $response = $this->get('/api/v1/me', [], [
            'X-API-Key' => $plain_key,
        ]);

        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);
        $response->assertJsonPath('data.id', $user->id);
    }

    public function test_invalid_api_key_returns_401(): void
    {
        $response = $this->get('/api/v1/me', [], [
            'X-API-Key' => 'invalid_key_that_does_not_exist',
        ]);

        $response->assertStatus(401);
    }

    public function test_disabled_api_key_returns_401(): void
    {
        $user = $this->createTestUser();

        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Disabled Key',
        ], $this->authHeaders($user));

        $plain_key = $create_response->json('data.plain_key');
        $key_id = $create_response->json('data.api_key.id');

        // 禁用 key
        $api_key = ApiKey::query()->find($key_id);
        $api_key->enabled = false;
        $api_key->save();

        // 使用已禁用的 key
        $response = $this->get('/api/v1/me', [], [
            'X-API-Key' => $plain_key,
        ]);

        $response->assertStatus(403);
    }

    public function test_jwt_takes_priority_over_api_key(): void
    {
        $user_a = $this->createTestUser();
        $user_b = $this->createTestUser();

        // user_b 创建 API Key
        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Priority Test Key',
        ], $this->authHeaders($user_b));

        $plain_key = $create_response->json('data.plain_key');

        // 同时携带 JWT (user_a) 和 API Key (user_b)，应使用 JWT
        $response = $this->get('/api/v1/me', [], [
            'Authorization' => 'Bearer ' . $this->getAuthToken($user_a),
            'X-API-Key' => $plain_key,
        ]);

        $response->assertStatus(200);
        $response->assertJsonPath('data.id', $user_a->id);
    }

    public function test_api_key_updates_last_used_at(): void
    {
        $user = $this->createTestUser();

        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'LastUsed Test Key',
        ], $this->authHeaders($user));

        $plain_key = $create_response->json('data.plain_key');
        $key_id = $create_response->json('data.api_key.id');

        // 使用 API Key 访问
        $this->get('/api/v1/me', [], [
            'X-API-Key' => $plain_key,
        ]);

        // 验证 last_used_at 已更新
        $api_key = ApiKey::query()->find($key_id);
        $this->assertNotNull($api_key->last_used_at);
    }

    public function test_disabled_user_api_key_returns_403(): void
    {
        $user = $this->createTestUser();

        $create_response = $this->post('/api/v1/me/api-keys', [
            'name' => 'Disabled User Key',
        ], $this->authHeaders($user));

        $plain_key = $create_response->json('data.plain_key');

        // 禁用用户
        $user->status = 0;
        $user->save();

        // 使用 API Key 访问
        $response = $this->get('/api/v1/me', [], [
            'X-API-Key' => $plain_key,
        ]);

        $response->assertStatus(403);
    }

    public function test_no_credentials_returns_401(): void
    {
        $response = $this->get('/api/v1/me');

        $response->assertStatus(401);
    }
}
