<?php

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

uses(RefreshDatabase::class);

function makePlatformRoleBoundaryToken(string $username): string
{
    $admin = AdminUser::query()->create([
        'username' => $username,
        'name' => 'Role Boundary Manager',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    $role = AdminRole::query()->create([
        'slug' => 'boundary_'.$username,
        'name' => 'Boundary Manager',
        'scope_type' => AdminRole::SCOPE_SYSTEM,
    ]);
    $role->syncLegacyPermissionSlugs(['prd.admin_user.manage', 'prd.admin_role.manage']);
    $admin->roles()->sync([
        (int) $role->id => [
            'site_id' => AdminUser::requireDefaultAdminSiteId(),
            'granted_at' => now(),
        ],
    ]);

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

function createPlatformRoleBoundaryAgent(string $username): AdminUser
{
    $siteId = AdminUser::requireDefaultAdminSiteId();
    $root = AgentNode::query()
        ->where('admin_site_id', $siteId)
        ->where('depth', 0)
        ->firstOrFail();
    $super = AdminUser::query()->create([
        'username' => 'super_'.$username,
        'name' => 'Super',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($super);

    $node = app(AgentNodeService::class)->createChild($super, [
        'parent_id' => (int) $root->id,
        'code' => 'node-'.$username,
        'name' => 'Agent '.$username,
        'username' => $username,
        'password' => 'secret-strong',
    ]);

    $adminUserId = DB::table('admin_user_agents')
        ->where('agent_node_id', $node->id)
        ->where('is_primary', true)
        ->value('admin_user_id');

    return AdminUser::query()->findOrFail((int) $adminUserId);
}

test('permission catalog keeps all roles but only exposes platform assignable roles', function (): void {
    $token = makePlatformRoleBoundaryToken('catalog_boundary');

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

    expect(collect($data['roles'])->pluck('slug')->all())
        ->toContain(PlatformSystemRoles::SLUG_AGENT, PlatformSystemRoles::SLUG_SUPER_ADMIN);
    expect(collect($data['assignable_roles'])->pluck('slug')->all())
        ->not->toContain(PlatformSystemRoles::SLUG_AGENT, PlatformSystemRoles::SLUG_SUPER_ADMIN);
});

test('platform account create and role update reject the agent role', function (): void {
    $token = makePlatformRoleBoundaryToken('assignment_boundary');
    $siteId = AdminUser::requireDefaultAdminSiteId();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->withHeader('X-Locale', 'zh')
        ->postJson('/api/v1/admin/admin-users', [
            'username' => 'illegal_agent_platform',
            'nickname' => 'Illegal Agent Platform',
            'email' => null,
            'password' => 'secret-strong',
            'status' => 0,
            'admin_site_id' => $siteId,
            'role_slugs' => [PlatformSystemRoles::SLUG_AGENT],
        ])
        ->assertStatus(422)
        ->assertJsonPath('code', ErrorCode::ValidationFailed->value)
        ->assertJsonPath('msg', '平台账号不能分配“代理”角色；请在“代理管理”中创建或绑定代理账号。');

    expect(AdminUser::query()->where('username', 'illegal_agent_platform')->exists())->toBeFalse();

    $target = AdminUser::query()->create([
        'username' => 'platform_role_target',
        'name' => 'Platform 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' => $siteId,
            'role_slugs' => [PlatformSystemRoles::SLUG_AGENT],
        ])
        ->assertStatus(422)
        ->assertJsonPath('code', ErrorCode::ValidationFailed->value);
});

test('agent creation keeps its agent role and cleanup removes only unbound platform assignments', function (): void {
    $agentUser = createPlatformRoleBoundaryAgent('legal_agent_boundary');
    $agentRole = AdminRole::query()
        ->where('scope_type', AdminRole::SCOPE_SYSTEM)
        ->where('slug', PlatformSystemRoles::SLUG_AGENT)
        ->firstOrFail();
    $siteId = AdminUser::requireDefaultAdminSiteId();

    expect(DB::table('admin_user_agents')->where('admin_user_id', $agentUser->id)->exists())->toBeTrue();
    expect(DB::table('admin_user_site_roles')
        ->where('admin_user_id', $agentUser->id)
        ->where('role_id', $agentRole->id)
        ->exists())->toBeTrue();

    $illegalUser = AdminUser::query()->create([
        'username' => 'unbound_agent_role',
        'name' => 'Unbound Agent Role',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    DB::table('admin_user_site_roles')->insert([
        'admin_user_id' => $illegalUser->id,
        'site_id' => $siteId,
        'role_id' => $agentRole->id,
        'granted_at' => now(),
    ]);

    $otherSiteId = (int) DB::table('admin_sites')->insertGetId([
        'code' => 'boundary-other-site',
        'name' => 'Boundary Other Site',
        'is_default' => false,
        'created_at' => now(),
        'updated_at' => now(),
    ]);
    DB::table('admin_user_site_roles')->insert([
        'admin_user_id' => $agentUser->id,
        'site_id' => $otherSiteId,
        'role_id' => $agentRole->id,
        'granted_at' => now(),
    ]);
    $ordinaryRole = AdminRole::query()->create([
        'slug' => 'cross_site_ordinary_role',
        'name' => 'Cross Site Ordinary Role',
        'scope_type' => AdminRole::SCOPE_SYSTEM,
    ]);
    DB::table('admin_user_site_roles')->insert([
        'admin_user_id' => $agentUser->id,
        'site_id' => $otherSiteId,
        'role_id' => $ordinaryRole->id,
        'granted_at' => now(),
    ]);

    expect(InvalidPlatformAgentRoleCleanup::run())->toBe(2);
    expect(DB::table('admin_user_site_roles')
        ->where('admin_user_id', $illegalUser->id)
        ->where('role_id', $agentRole->id)
        ->exists())->toBeFalse();
    expect(DB::table('admin_user_site_roles')
        ->where('admin_user_id', $agentUser->id)
        ->where('site_id', $siteId)
        ->where('role_id', $agentRole->id)
        ->exists())->toBeTrue();
    expect(DB::table('admin_user_site_roles')
        ->where('admin_user_id', $agentUser->id)
        ->where('site_id', $otherSiteId)
        ->where('role_id', $agentRole->id)
        ->exists())->toBeFalse();
    expect(DB::table('admin_user_site_roles')
        ->where('admin_user_id', $agentUser->id)
        ->where('site_id', $otherSiteId)
        ->where('role_id', $ordinaryRole->id)
        ->exists())->toBeTrue();
    expect(AdminUser::query()->whereKey($illegalUser->id)->exists())->toBeTrue();
});

test('role counts classify platform and agent accounts and role filter never leaks agents', function (): void {
    $token = makePlatformRoleBoundaryToken('count_boundary');
    $siteId = AdminUser::requireDefaultAdminSiteId();
    $role = AdminRole::query()->create([
        'slug' => 'counted_role',
        'name' => 'Counted Role',
        'scope_type' => AdminRole::SCOPE_SYSTEM,
    ]);
    $platformUser = AdminUser::query()->create([
        'username' => 'counted_platform',
        'name' => 'Counted Platform',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    $platformUser->roles()->sync([
        (int) $role->id => [
            'site_id' => $siteId,
            'granted_at' => now(),
        ],
    ]);
    $agentUser = createPlatformRoleBoundaryAgent('counted_agent');
    DB::table('admin_user_site_roles')->insert([
        'admin_user_id' => $agentUser->id,
        'site_id' => $siteId,
        'role_id' => $role->id,
        'granted_at' => now(),
    ]);

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

    expect($roleRow)
        ->not->toBeNull()
        ->and($roleRow['platform_user_count'])->toBe(1)
        ->and($roleRow['agent_user_count'])->toBe(1)
        ->and($roleRow['user_count'])->toBe(2);

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

    expect(collect($items)->pluck('username')->all())
        ->toBe(['counted_platform'])
        ->not->toContain('counted_agent');
});
