<?php

use App\Models\AdminUser;
use App\Models\AgentNode;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    ensureAdminActionCatalogSeeded();
    $this->artisan('lottery:admin-auth-sync')->assertExitCode(0);
});

function grantDelegationAgentOperator(AdminUser $admin, AgentNode $agent, bool $manage = true): void
{
    $now = now();
    $roleId = DB::table('admin_roles')->insertGetId([
        'slug' => 'agent_ops_'.$admin->id,
        'code' => 'agent_ops_'.$admin->id,
        'name' => 'Agent Ops',
        'description' => null,
        'status' => 1,
        'is_system' => false,
        'sort_order' => 0,
        'created_at' => $now,
        'updated_at' => $now,
    ]);

    $codes = $manage
        ? [
            'agent.node.view',
            'agent.node.manage',
            'agent.role.view',
            'agent.role.manage',
            'agent.user.view',
            'agent.user.manage',
        ]
        : ['agent.node.view'];

    $actionIds = DB::table('admin_menu_actions')
        ->whereIn('permission_code', $codes)
        ->pluck('id');

    foreach ($actionIds as $actionId) {
        DB::table('admin_role_menu_actions')->insert([
            'role_id' => $roleId,
            'menu_action_id' => (int) $actionId,
        ]);
    }

    DB::table('admin_user_site_roles')->insert([
        'admin_user_id' => $admin->id,
        'site_id' => (int) $agent->admin_site_id,
        'role_id' => $roleId,
        'granted_at' => $now,
    ]);

    DB::table('admin_user_agents')->insert([
        'admin_user_id' => $admin->id,
        'agent_node_id' => (int) $agent->id,
        'is_primary' => true,
        'granted_at' => $now,
    ]);

    DB::table('admin_user_agent_roles')->insert([
        'admin_user_id' => $admin->id,
        'agent_node_id' => (int) $agent->id,
        'role_id' => $roleId,
        'granted_at' => $now,
    ]);
}

test('parent agent can sync delegation grants for direct child', function (): void {
    $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id');
    $rootId = (int) DB::table('agent_nodes')->where('admin_site_id', $siteId)->where('depth', 0)->value('id');
    $service = app(\App\Services\Agent\AgentNodeService::class);
    $super = AdminUser::query()->create([
        'username' => 'deleg_super',
        'name' => 'Super',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($super);

    $parent = $service->createChild($super, agentChildPayload([
        'parent_id' => $rootId,
        'code' => 'deleg-parent',
        'name' => 'Deleg Parent',
    ]));
    $child = $service->createChild($super, agentChildPayload([
        'parent_id' => $parent->id,
        'code' => 'deleg-child',
        'name' => 'Deleg Child',
    ]));

    $parentAdmin = AdminUser::query()->create([
        'username' => 'deleg_parent_admin',
        'name' => 'Parent Admin',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantDelegationAgentOperator($parentAdmin, $parent, manage: true);

    $viewActionId = (int) DB::table('admin_menu_actions')
        ->where('permission_code', 'agent.node.view')
        ->value('id');

    $token = $parentAdmin->createToken('test', ['*'], now()->addDay())->plainTextToken;

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->putJson('/api/v1/admin/agent-nodes/'.$child->id.'/delegation-grants', [
            'grants' => [
                ['menu_action_id' => $viewActionId, 'can_delegate' => true],
            ],
        ])
        ->assertOk()
        ->assertJsonPath('data.child_agent_id', $child->id)
        ->assertJsonCount(1, 'data.grants');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/agent-nodes/'.$child->id.'/delegation-grants')
        ->assertOk()
        ->assertJsonPath('data.grants.0.can_delegate', true);
});

test('delegation ceiling blocks role permissions beyond child grants', function (): void {
    $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id');
    $rootId = (int) DB::table('agent_nodes')->where('admin_site_id', $siteId)->where('depth', 0)->value('id');
    $service = app(\App\Services\Agent\AgentNodeService::class);
    $super = AdminUser::query()->create([
        'username' => 'ceil_super',
        'name' => 'Super',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($super);

    $branch = $service->createChild($super, agentChildPayload([
        'parent_id' => $rootId,
        'code' => 'ceil-branch',
        'name' => 'Ceil Branch',
    ]));

    $viewActionId = (int) DB::table('admin_menu_actions')
        ->where('permission_code', 'agent.node.view')
        ->value('id');

    DB::table('agent_delegation_grants')->insert([
        'parent_agent_id' => $rootId,
        'child_agent_id' => $branch->id,
        'menu_action_id' => $viewActionId,
        'can_delegate' => true,
        'granted_at' => now(),
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $operator = AdminUser::query()->create([
        'username' => 'ceil_ops',
        'name' => 'Ops',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantDelegationAgentOperator($operator, $branch, manage: true);
    $token = $operator->createToken('test', ['*'], now()->addDay())->plainTextToken;

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/agent-nodes/'.$branch->id.'/roles', [
            'slug' => 'ceil_ok',
            'name' => 'OK',
            'permission_slugs' => ['prd.agent.view'],
        ])
        ->assertOk();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/agent-nodes/'.$branch->id.'/roles', [
            'slug' => 'ceil_bad',
            'name' => 'Bad',
            'permission_slugs' => ['prd.dashboard.view'],
        ])
        ->assertStatus(422);
});

test('auth me includes delegation ceiling for agent user', function (): void {
    $siteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id');
    $root = AgentNode::query()->where('admin_site_id', $siteId)->where('depth', 0)->firstOrFail();
    $admin = AdminUser::query()->create([
        'username' => 'me_deleg',
        'name' => 'Me',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantDelegationAgentOperator($admin, $root, manage: false);
    $token = $admin->createToken('test', ['*'], now()->addDay())->plainTextToken;

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/auth/me')
        ->assertOk()
        ->assertJsonStructure(['data' => ['admin' => ['delegation_ceiling', 'operational_permissions']]]);
});
