<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Integration\Permission;

use App\Model\Role;
use App\Model\RoleRouteOverride;
use App\Model\Route;
use App\Model\RouteGroup;
use App\Model\Store;
use App\Model\User;
use App\Model\UserDataScope;
use Hyperf\DbConnection\Db;
use HyperfTest\TestCase;
use Qbhy\HyperfAuth\AuthManager;

use function Hyperf\Support\make;

/**
 * 权限中间件端到端集成测试
 *
 * 覆盖 AuthMiddleware → PermissionMiddleware → Controller 完整链路
 *
 * @internal
 * @coversNothing
 */
class PermissionFlowTest 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(string $role_name, array $overrides = []): User
    {
        $role = Role::query()->where('name', $role_name)->firstOrFail();
        $suffix = bin2hex(random_bytes(4));

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

    /**
     * 为指定角色授权指定路由的路由组
     *
     * @return array{group: RouteGroup, old_group_id: ?int} 用于清理
     */
    protected function authorizeRouteGroup(int $role_id, Route $route): array
    {
        $group = RouteGroup::query()->create([
            'name' => 'integ_test_' . uniqid(),
            'label' => '集成测试路由组',
        ]);

        $old_group_id = $route->group_id;
        $route->group_id = $group->id;
        $route->save();

        Db::table('role_route_groups')->insert([
            'role_id' => $role_id,
            'group_id' => $group->id,
        ]);

        return ['group' => $group, 'old_group_id' => $old_group_id];
    }

    /**
     * 清理路由组授权
     */
    protected function cleanupRouteGroup(int $role_id, Route $route, array $auth_data): void
    {
        Db::table('role_route_groups')
            ->where('role_id', $role_id)
            ->where('group_id', $auth_data['group']->id)
            ->delete();
        $route->group_id = $auth_data['old_group_id'];
        $route->save();
        $auth_data['group']->delete();
    }

    // ========== 测试用例 ==========

    public function test_admin_full_access(): void
    {
        $user = $this->createTestUser('administrator');

        // administrator 可访问全部 UserController 端点
        $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
        $response->assertStatus(200);
        $response->assertJsonPath('code', 0);

        // 参数化路由
        $response = $this->get('/api/v1/users/1', [], $this->authHeaders($user));
        $this->assertContains($response->getStatusCode(), [200, 404]);
    }

    public function test_developer_with_group_auth_access(): void
    {
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        $auth_data = $this->authorizeRouteGroup($user->role_id, $route);

        try {
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(200);
        } finally {
            $this->cleanupRouteGroup($user->role_id, $route, $auth_data);
        }
    }

    public function test_accessor_without_auth_denied(): void
    {
        $user = $this->createTestUser('accessor');

        // accessor 无任何路由授权 → 403
        $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
        $response->assertStatus(403);
    }

    public function test_accessor_with_override_access(): void
    {
        $user = $this->createTestUser('accessor');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 添加 store scope 以通过数据范围检查
        $store = Store::query()->first();
        if ($store) {
            UserDataScope::query()->create([
                'user_id' => $user->id,
                'scope_type' => 'store',
                'scope_id' => $store->id,
            ]);
        }

        // 设置 override 允许访问
        RoleRouteOverride::query()->create([
            'role_id' => $user->role_id,
            'route_id' => $route->id,
            'allowed' => true,
        ]);

        try {
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(200);
        } finally {
            RoleRouteOverride::query()
                ->where('role_id', $user->role_id)
                ->where('route_id', $route->id)
                ->delete();
            UserDataScope::query()->where('user_id', $user->id)->delete();
        }
    }

    public function test_override_deny_overrides_group(): void
    {
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 授权路由组
        $auth_data = $this->authorizeRouteGroup($user->role_id, $route);

        // 设置 override 为拒绝（优先级高于 group）
        RoleRouteOverride::query()->create([
            'role_id' => $user->role_id,
            'route_id' => $route->id,
            'allowed' => false,
        ]);

        try {
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            RoleRouteOverride::query()
                ->where('role_id', $user->role_id)
                ->where('route_id', $route->id)
                ->delete();
            $this->cleanupRouteGroup($user->role_id, $route, $auth_data);
        }
    }

    public function test_developer_scope_param_validation(): void
    {
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 授权路由访问
        $auth_data = $this->authorizeRouteGroup($user->role_id, $route);

        try {
            // 传不属于自己的 platform_id → 403
            $response = $this->get('/api/v1/users', ['platform_id' => 999999], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            $this->cleanupRouteGroup($user->role_id, $route, $auth_data);
        }
    }

    // ========== 边缘场景 ==========

    public function test_accessor_with_invalid_store_id_denied(): void
    {
        $user = $this->createTestUser('accessor');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 授权路由访问
        RoleRouteOverride::query()->create([
            'role_id' => $user->role_id,
            'route_id' => $route->id,
            'allowed' => true,
        ]);

        // 添加 store scope（store_id=1），然后传不在 bitmap 中的 store_id
        $store = Store::query()->first();
        if (!$store) {
            $this->markTestSkipped('stores 表中无数据');
        }

        UserDataScope::query()->create([
            'user_id' => $user->id,
            'scope_type' => 'store',
            'scope_id' => $store->id,
        ]);

        try {
            // 传不属于自己的 store_id → 403
            $response = $this->get('/api/v1/users', ['store_id' => 999999], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            RoleRouteOverride::query()
                ->where('role_id', $user->role_id)
                ->where('route_id', $route->id)
                ->delete();
            UserDataScope::query()->where('user_id', $user->id)->delete();
        }
    }

    public function test_accessor_with_invalid_company_id_denied(): void
    {
        $user = $this->createTestUser('accessor');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        RoleRouteOverride::query()->create([
            'role_id' => $user->role_id,
            'route_id' => $route->id,
            'allowed' => true,
        ]);

        $store = Store::query()->first();
        if (!$store) {
            $this->markTestSkipped('stores 表中无数据');
        }

        UserDataScope::query()->create([
            'user_id' => $user->id,
            'scope_type' => 'store',
            'scope_id' => $store->id,
        ]);

        try {
            // 传不属于自己的 company_id → 403
            $response = $this->get('/api/v1/users', ['company_id' => 999999], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            RoleRouteOverride::query()
                ->where('role_id', $user->role_id)
                ->where('route_id', $route->id)
                ->delete();
            UserDataScope::query()->where('user_id', $user->id)->delete();
        }
    }

    public function test_accessor_with_no_scope_data_gets_empty_results(): void
    {
        $user = $this->createTestUser('accessor');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 授权路由访问但不设置任何 scope 数据
        RoleRouteOverride::query()->create([
            'role_id' => $user->role_id,
            'route_id' => $route->id,
            'allowed' => true,
        ]);

        try {
            // accessor 无 scope 数据 → bitmap 为空 → scope_ids=[] → 200 但无数据
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(200);
            $response->assertJsonPath('code', 0);
        } finally {
            RoleRouteOverride::query()
                ->where('role_id', $user->role_id)
                ->where('route_id', $route->id)
                ->delete();
        }
    }

    public function test_developer_with_invalid_store_id_denied(): void
    {
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        $auth_data = $this->authorizeRouteGroup($user->role_id, $route);

        try {
            // developer 传不属于自己的 store_id → 403
            $response = $this->get('/api/v1/users', ['store_id' => 999999], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            $this->cleanupRouteGroup($user->role_id, $route, $auth_data);
        }
    }

    public function test_developer_with_no_platforms_gets_empty_scope(): void
    {
        // 创建 developer 用户但不关联任何 platform
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        $auth_data = $this->authorizeRouteGroup($user->role_id, $route);

        try {
            // developer 无维护平台 → platform_ids=[] → scope_ids=[] → 200 但无数据
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(200);
            $response->assertJsonPath('code', 0);
        } finally {
            $this->cleanupRouteGroup($user->role_id, $route, $auth_data);
        }
    }

    public function test_ungrouped_route_denied_for_non_admin(): void
    {
        $user = $this->createTestUser('developer');

        $route = Route::query()->where('method', 'GET')->where('path', '/api/v1/users')->first();
        if (!$route) {
            $this->markTestSkipped('routes 表中无 GET /api/v1/users 路由');
        }

        // 确保路由未分组且无 override
        $old_group_id = $route->group_id;
        $route->group_id = null;
        $route->save();

        try {
            // 路由已注册但未分组、无 override → 403
            $response = $this->get('/api/v1/users', [], $this->authHeaders($user));
            $response->assertStatus(403);
        } finally {
            $route->group_id = $old_group_id;
            $route->save();
        }
    }
}
