<?php

use App\Models\AdminRole;
use App\Models\AdminUser;
use App\Models\AgentNode;
use App\Lottery\ErrorCode;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use App\Support\AdminPermissionBridge;
use App\Services\Agent\AgentNodeService;
use App\Services\Agent\AgentAdminUserService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

function makeAdminWithPermissions(string $username, array $permissionSlugs): string
{
    $admin = AdminUser::query()->create([
        'username' => $username,
        'name' => 'Tester',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);

    $role = AdminRole::query()->create([
        'slug' => 'role_'.$username,
        'name' => 'Role '.$username,
    ]);

    $role->syncLegacyPermissionSlugs($permissionSlugs);

    $siteId = AdminUser::defaultAdminSiteId();
    $admin->roles()->sync([
        (int) $role->id => [
            'site_id' => $siteId,
            'granted_at' => now(),
        ],
    ]);

    return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken;
}

test('admin user permission apis require rbac permission', function (): void {
    $token = makeAdminWithPermissions('rbac_viewer', ['prd.report.view']);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-users')
        ->assertForbidden()
        ->assertJsonPath('code', ErrorCode::AdminForbidden->value);
});

test('admin can list platform users and read effective permissions', function (): void {
    $token = makeAdminWithPermissions('rbac_manager', ['prd.admin_user.manage']);

    $target = AdminUser::query()->create([
        'username' => 'target_user',
        'name' => 'Target User',
        'email' => 'target@example.com',
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    $targetRole = AdminRole::query()->create(['slug' => 'target_role', 'name' => 'Target Role']);

    $drawCodes = AdminPermissionBridge::menuActionCodesForLegacy('prd.draw_result.view');
    $drawIds = DB::table('admin_menu_actions')
        ->whereIn('permission_code', $drawCodes)
        ->where('status', 1)
        ->pluck('id')
        ->all();
    foreach ($drawIds as $mid) {
        DB::table('admin_role_menu_actions')->insert([
            'role_id' => $targetRole->id,
            'menu_action_id' => (int) $mid,
        ]);
    }

    $siteId = AdminUser::defaultAdminSiteId();
    $target->roles()->sync([
        (int) $targetRole->id => [
            'site_id' => $siteId,
            'granted_at' => now(),
        ],
    ]);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-user-permission-catalog')
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonFragment(['slug' => 'prd.admin_user.manage']);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-users?keyword=target')
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonPath('data.items.0.username', 'target_user')
        ->assertJsonPath('data.items.0.roles.0', 'target_role');

    $list = $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-users?keyword=target')
        ->assertOk()
        ->json('data.items.0.effective_permissions');

    expect($list)->toContain('prd.draw_result.view');
});

test('admin can sync user roles for default site', function (): void {
    $token = makeAdminWithPermissions('rbac_role_editor', ['prd.admin_user.manage', 'prd.admin_role.manage']);

    $r1 = AdminRole::query()->create(['slug' => 'role_sync_a', 'name' => 'Role A']);
    $r2 = AdminRole::query()->create(['slug' => 'role_sync_b', 'name' => 'Role B']);

    $target = AdminUser::query()->create([
        'username' => 'role_target',
        'name' => 'Role Target',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-users/'.$target->id.'/roles', [
            'admin_site_id' => AdminUser::defaultAdminSiteId(),
            'role_slugs' => ['role_sync_b', 'role_sync_a'],
        ])
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value);

    $slugs = $target->fresh()->adminRoleSlugs();
    sort($slugs);
    expect($slugs)->toBe(['role_sync_a', 'role_sync_b']);
});

test('platform admin users list excludes agent accounts', function (): void {
    $token = makeAdminWithPermissions('platform_list_manager', ['prd.admin_user.manage']);

    $platformUser = AdminUser::query()->create([
        'username' => 'platform_only_user',
        'name' => 'Platform Only',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);

    $siteId = AdminUser::defaultAdminSiteId();
    $root = AgentNode::query()->where('admin_site_id', $siteId)->where('depth', 0)->firstOrFail();

    $super = AdminUser::query()->create([
        'username' => 'agent_list_super',
        'name' => 'Super',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($super);

    $branch = app(AgentNodeService::class)->createChild($super, [
        'parent_id' => (int) $root->id,
        'code' => 'list-branch',
        'name' => 'List Branch',
    ]);

    app(AgentAdminUserService::class)->createUnderAgent($branch, [
        'username' => 'agent_hidden_user',
        'nickname' => 'Agent Hidden',
        'password' => 'secret-strong-2',
        'role_ids' => [],
    ]);

    $items = $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-users')
        ->assertOk()
        ->json('data.items');

    expect(collect($items)->pluck('username')->all())
        ->toContain('platform_only_user')
        ->not->toContain('agent_hidden_user');
});

test('platform account apis reject agent accounts and agent roles', function (): void {
    $token = makeAdminWithPermissions('platform_scope_manager', ['prd.admin_user.manage', 'prd.admin_role.manage']);

    $siteId = AdminUser::defaultAdminSiteId();
    $root = AgentNode::query()->where('admin_site_id', $siteId)->where('depth', 0)->firstOrFail();

    $super = AdminUser::query()->create([
        'username' => 'platform_guard_super',
        'name' => 'Super',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($super);

    $branch = app(AgentNodeService::class)->createChild($super, [
        'parent_id' => (int) $root->id,
        'code' => 'platform-guard-branch',
        'name' => 'Platform Guard Branch',
    ]);

    $agentRole = AdminRole::query()->create([
        'slug' => 'guard_agent_role',
        'name' => 'Guard Agent Role',
        'scope_type' => AdminRole::SCOPE_AGENT,
        'owner_agent_id' => $branch->id,
    ]);
    $agentRole->syncLegacyPermissionSlugs(['prd.agent.role.view']);

    $agentUser = app(AgentAdminUserService::class)->createUnderAgent($branch, [
        'username' => 'guard_agent_user',
        'nickname' => 'Guard Agent User',
        'password' => 'secret-strong-3',
        'role_ids' => [(int) $agentRole->id],
    ]);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-users/'.$agentUser->id.'/roles', [
            'role_slugs' => ['guard_agent_role'],
        ])
        ->assertStatus(422);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-roles/'.$agentRole->id.'/permissions', [
            'permission_slugs' => ['prd.admin_role.manage'],
        ])
        ->assertStatus(422);
});

test('permission catalog groups permissions by admin navigation order', function (): void {
    $token = makeAdminWithPermissions('nav_group_catalog', ['prd.admin_user.manage', 'prd.admin_role.manage']);

    $groups = $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-user-permission-catalog')
        ->assertOk()
        ->json('data.permission_menu_groups');

    expect(array_column($groups, 'key'))->toBe([
        'dashboard',
        'agents',
        'draws',
        'tickets',
        'players',
        'settlement',
        'wallet',
        'reconcile',
        'reports',
        'rules_plays',
        'rules_odds',
        'jackpot',
        'risk_cap',
        'currencies',
        'integration',
        'admin_users',
        'admin_roles',
        'audit',
        'settings',
        'risk',
    ]);
    expect($groups[1]['key'])->toBe('agents');
    expect($groups[2]['key'])->toBe('draws');

    $groupsByKey = collect($groups)->keyBy('key');
    expect($groupsByKey['admin_users']['label'])->toBe('平台账号');
    expect(array_column($groupsByKey['admin_users']['permissions'], 'slug'))->toBe(['prd.admin_user.manage']);
    expect($groupsByKey['admin_roles']['label'])->toBe('角色管理');
    expect(array_column($groupsByKey['admin_roles']['permissions'], 'slug'))->toBe(['prd.admin_role.manage']);
    expect(array_column($groupsByKey['tickets']['permissions'], 'slug'))->toBe([
        'prd.tickets.view',
    ]);
    expect(array_column($groupsByKey['reports']['permissions'], 'slug'))->toContain(
        'prd.report.view',
    );
    expect(array_column($groupsByKey['jackpot']['permissions'], 'slug'))->toContain(
        'prd.jackpot.manage',
        'prd.jackpot.view',
    );
    expect(array_column($groupsByKey['reconcile']['permissions'], 'slug'))->toBe([
        'prd.wallet_reconcile.manage',
        'prd.wallet_reconcile.view',
        'prd.wallet_reconcile.view_cs',
    ]);
});

test('admin can adjust platform agent role permissions from the catalog', function (): void {
    $token = makeAdminWithPermissions('role_permission_repairer', ['prd.admin_user.manage', 'prd.admin_role.manage']);

    $catalog = $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-user-permission-catalog')
        ->assertOk()
        ->json('data');

    $catalogSlugs = collect($catalog['permission_menu_groups'])
        ->flatMap(static fn (array $group): array => array_column($group['permissions'], 'slug'))
        ->unique()
        ->values()
        ->all();

    expect($catalogSlugs)
        ->toContain('prd.admin_user.manage')
        ->toContain('prd.admin_role.manage')
        ->toContain('prd.report.view')
        ->toContain('prd.wallet_reconcile.manage');

    $agentRole = collect($this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-roles')
        ->assertOk()
        ->json('data.items'))
        ->firstWhere('slug', 'agent');

    expect($agentRole)->not->toBeNull();

    $repairResponse = $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-roles/'.$agentRole['id'].'/permissions', [
            'permission_slugs' => ['prd.report.view', 'prd.wallet_reconcile.manage'],
        ])
        ->assertOk()
        ->assertJsonPath('data.slug', 'agent');

    expect($repairResponse->json('data.permission_slugs'))
        ->toContain('prd.report.view', 'prd.wallet_reconcile.manage');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-roles/'.$agentRole['id'].'/permissions', [
            'permission_slugs' => ['prd.admin_role.manage'],
        ])
        ->assertOk()
        ->assertJsonPath('data.permission_slugs', ['prd.admin_role.manage']);

    $persistedRole = collect($this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/admin-roles')
        ->assertOk()
        ->json('data.items'))
        ->firstWhere('slug', 'agent');

    expect($persistedRole['permission_slugs'])->toBe(['prd.admin_role.manage']);
});

test('admin can create update and delete users with crud rules', function (): void {
    $token = makeAdminWithPermissions('crud_actor', ['prd.admin_user.manage']);

    $crudRole = AdminRole::query()->create(['slug' => 'crud_new_user_role', 'name' => 'Crud Role']);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/admin-users', [
            'username' => 'NewUser_XX',
            'nickname' => '新用户',
            'email' => 'newuser@example.com',
            'password' => 'secret-long',
            'status' => 0,
            'admin_site_id' => AdminUser::defaultAdminSiteId(),
            'role_slugs' => ['crud_new_user_role'],
        ])
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonPath('data.username', 'newuser_xx')
        ->assertJsonPath('data.roles.0', 'crud_new_user_role');

    $created = AdminUser::query()->where('username', 'newuser_xx')->firstOrFail();
    expect($created->adminRoleSlugs())->toContain('crud_new_user_role');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/admin-users', [
            'username' => 'newuser_xx',
            'nickname' => 'dup',
            'email' => null,
            'password' => 'secret-long',
            'admin_site_id' => AdminUser::defaultAdminSiteId(),
            'role_slugs' => [$crudRole->slug],
        ])
        ->assertStatus(422)
        ->assertJsonPath('code', ErrorCode::ValidationFailed->value);

    $target = AdminUser::query()->where('username', 'newuser_xx')->firstOrFail();
    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/admin-users/'.$target->id, [
            'nickname' => '已改名',
            'email' => null,
            'password' => 'new-secret-9',
        ])
        ->assertOk()
        ->assertJsonPath('data.nickname', '已改名');

    expect(Hash::check('new-secret-9', $target->fresh()->password))->toBeTrue();

    $victim = AdminUser::query()->create([
        'username' => 'to_delete',
        'name' => 'Delete Me',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->deleteJson('/api/v1/admin/admin-users/'.$victim->id)
        ->assertOk()
        ->assertJsonPath('data.deleted', true);

    expect(AdminUser::query()->whereKey($victim->id)->exists())->toBeFalse();
});

test('admin user create requires at least one role slug', function (): void {
    $token = makeAdminWithPermissions('create_need_roles', ['prd.admin_user.manage']);
    AdminRole::query()->create(['slug' => 'role_for_create_gate', 'name' => 'Gate Role']);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/admin-users', [
            'username' => 'no_roles_user',
            'nickname' => 'NR',
            'email' => null,
            'password' => 'secret-long',
            'admin_site_id' => AdminUser::defaultAdminSiteId(),
            'role_slugs' => [],
        ])
        ->assertStatus(422)
        ->assertJsonPath('code', ErrorCode::ValidationFailed->value);
});

test('admin cannot delete self', function (): void {
    $token = makeAdminWithPermissions('self_guard', ['prd.admin_user.manage']);
    $me = AdminUser::query()->where('username', 'self_guard')->firstOrFail();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->withHeader('X-Locale', 'zh')
        ->deleteJson('/api/v1/admin/admin-users/'.$me->id)
        ->assertStatus(422)
        ->assertJsonPath('code', ErrorCode::ValidationFailed->value)
        ->assertJsonPath('msg', '不能删除当前登录账号。');
});

test('admin cannot delete the last super admin', function (): void {
    $token = makeAdminWithPermissions('super_deleter', ['prd.admin_user.manage']);

    $s1 = AdminUser::query()->create([
        'username' => 'super_one',
        'name' => 'S1',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($s1);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->withHeader('X-Locale', 'zh')
        ->deleteJson('/api/v1/admin/admin-users/'.$s1->id)
        ->assertStatus(422)
        ->assertJsonPath('msg', '不能删除最后一个超级管理员。');

    $s2 = AdminUser::query()->create([
        'username' => 'super_two',
        'name' => 'S2',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($s2);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->withHeader('X-Locale', 'zh')
        ->deleteJson('/api/v1/admin/admin-users/'.$s1->id)
        ->assertOk();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->withHeader('X-Locale', 'zh')
        ->deleteJson('/api/v1/admin/admin-users/'.$s2->id)
        ->assertStatus(422)
        ->assertJsonPath('msg', '不能删除最后一个超级管理员。');
});
