<?php

use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Services\Agent\AgentProfileService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;

uses(RefreshDatabase::class);

function createAgentLineForAllocation(string $code, int $creditLimit): AgentNode
{
    $siteId = (int) DB::table('admin_sites')->insertGetId([
        'code' => $code,
        'name' => $code,
        'is_default' => false,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $rootId = (int) DB::table('agent_nodes')->insertGetId([
        'admin_site_id' => $siteId,
        'parent_id' => null,
        'depth' => 0,
        'path' => '/'.$code,
        'code' => $code,
        'name' => 'Root',
        'status' => 1,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    AgentProfile::query()->create([
        'agent_node_id' => $rootId,
        'total_share_rate' => 60,
        'credit_limit' => $creditLimit,
        'allocated_credit' => 0,
        'used_credit' => 0,
        'rebate_limit' => 0.01,
        'default_player_rebate' => 0.005,
    ]);

    return AgentNode::query()->findOrFail($rootId);
}

test('player credit account syncs agent allocated credit', function (): void {
    $root = createAgentLineForAllocation('line-alloc', 10000);
    $service = app(AgentProfileService::class);

    $playerId = (int) DB::table('players')->insertGetId([
        'site_code' => 'line-alloc',
        'agent_node_id' => $root->id,
        'site_player_id' => 'p-alloc-1',
        'auth_source' => 'lottery_native',
        'funding_mode' => 'credit',
        'username' => 'alloc1',
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('player_credit_accounts')->insert([
        'player_id' => $playerId,
        'credit_limit' => 2000,
        'used_credit' => 0,
        'frozen_credit' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $service->refreshAllocatedCredit($root);

    $profile = AgentProfile::query()->where('agent_node_id', $root->id)->first();
    expect((int) $profile->allocated_credit)->toBe(2000);
    expect($service->present($profile)['available_credit'])->toBe(8000);
});

test('player credit allocation exceeds available throws', function (): void {
    $root = createAgentLineForAllocation('line-over', 5000);
    $service = app(AgentProfileService::class);

    expect(fn () => $service->assertMayIncreasePlayerCredit($root, 6000))
        ->toThrow(\Illuminate\Validation\ValidationException::class);
});

test('win loss does not change agent allocated credit', function (): void {
    $root = createAgentLineForAllocation('line-hold', 10000);

    $playerId = (int) DB::table('players')->insertGetId([
        'site_code' => 'line-hold',
        'agent_node_id' => $root->id,
        'site_player_id' => 'p-hold-1',
        'auth_source' => 'lottery_native',
        'funding_mode' => 'credit',
        'username' => 'hold1',
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('player_credit_accounts')->insert([
        'player_id' => $playerId,
        'credit_limit' => 2000,
        'used_credit' => 200,
        'frozen_credit' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    app(AgentProfileService::class)->refreshAllocatedCredit($root);

    $profile = AgentProfile::query()->where('agent_node_id', $root->id)->first();
    expect((int) $profile->allocated_credit)->toBe(2000);
});

test('raising player credit limit succeeds when agent allocated credit includes other subordinates', function (): void {
    $root = createAgentLineForAllocation('line-player-raise', 10000);
    $service = app(AgentProfileService::class);

    $playerId = (int) DB::table('players')->insertGetId([
        'site_code' => 'line-player-raise',
        'agent_node_id' => $root->id,
        'site_player_id' => 'p-other',
        'auth_source' => 'lottery_native',
        'funding_mode' => 'credit',
        'username' => 'other',
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('player_credit_accounts')->insert([
        'player_id' => $playerId,
        'credit_limit' => 3000,
        'used_credit' => 0,
        'frozen_credit' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $service->refreshAllocatedCredit($root);

    $newPlayerId = (int) DB::table('players')->insertGetId([
        'site_code' => 'line-player-raise',
        'agent_node_id' => $root->id,
        'site_player_id' => 'p-raise-1',
        'auth_source' => 'lottery_native',
        'funding_mode' => 'credit',
        'username' => 'raise1',
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('player_credit_accounts')->insert([
        'player_id' => $newPlayerId,
        'credit_limit' => 0,
        'used_credit' => 0,
        'frozen_credit' => 0,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $service->adjustPlayerCreditAllocation($root, 0, 2000);
    DB::table('player_credit_accounts')->where('player_id', $newPlayerId)->update(['credit_limit' => 2000]);
    $service->refreshAllocatedCredit($root);

    $profile = AgentProfile::query()->where('agent_node_id', $root->id)->first();
    expect((int) $profile->allocated_credit)->toBe(5000);
});
