<?php

use App\Models\Draw;
use App\Models\RiskPool;
use App\Models\AdminUser;
use App\Models\RiskPoolLockLog;
use Illuminate\Support\Facades\Hash;
use App\Services\Ticket\RiskPoolService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Exceptions\TicketOperationException;

uses(RefreshDatabase::class);

function mintRiskAdminToken(): string
{
    $admin = AdminUser::query()->create([
        'username' => 'risk_pool_admin',
        'name' => 'Risk QA',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($admin);

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

test('admin risk pools index returns rows for draw', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-001',
        'business_date' => '2026-05-12',
        'sequence_no' => 1,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '1234',
        'total_cap_amount' => 1_000_000,
        'locked_amount' => 200_000,
        'remaining_amount' => 800_000,
        'sold_out_status' => 0,
        'version' => 1,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '9999',
        'total_cap_amount' => 100,
        'locked_amount' => 100,
        'remaining_amount' => 0,
        'sold_out_status' => 1,
        'version' => 2,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools?per_page=10')
        ->assertOk()
        ->assertJsonPath('data.draw_no', '20260512-001')
        ->assertJsonPath('data.meta.total', 2);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools?sold_out_only=1')
        ->assertOk()
        ->assertJsonPath('data.meta.total', 1)
        ->assertJsonPath('data.items.0.normalized_number', '9999')
        ->assertJsonPath('data.items.0.is_sold_out', true);
});

test('admin risk pools index filters by number and high risk usage', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-004',
        'business_date' => '2026-05-12',
        'sequence_no' => 4,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '1288',
        'total_cap_amount' => 1_000,
        'locked_amount' => 850,
        'remaining_amount' => 150,
        'sold_out_status' => 0,
        'version' => 1,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '5678',
        'total_cap_amount' => 1_000,
        'locked_amount' => 100,
        'remaining_amount' => 900,
        'sold_out_status' => 0,
        'version' => 1,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools?high_risk_only=1')
        ->assertOk()
        ->assertJsonPath('data.meta.total', 1)
        ->assertJsonPath('data.items.0.normalized_number', '1288');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools?normalized_number=67')
        ->assertOk()
        ->assertJsonPath('data.meta.total', 1)
        ->assertJsonPath('data.items.0.normalized_number', '5678');
});

test('admin can manually close and recover a risk pool number', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-005',
        'business_date' => '2026-05-12',
        'sequence_no' => 5,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '2468',
        'total_cap_amount' => 1_000,
        'locked_amount' => 300,
        'remaining_amount' => 700,
        'sold_out_status' => 0,
        'version' => 1,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools/2468/manual-close')
        ->assertOk()
        ->assertJsonPath('data.normalized_number', '2468')
        ->assertJsonPath('data.is_sold_out', true)
        ->assertJsonPath('data.version', 2);

    $this->assertDatabaseHas('risk_pool_lock_logs', [
        'draw_id' => $draw->id,
        'normalized_number' => '2468',
        'action_type' => 'close',
        'amount' => 0,
        'source_reason' => 'admin_manual_close',
    ]);

    expect(fn () => app(RiskPoolService::class)->preview($draw->id, [['number_4d' => '2468', 'amount' => 1]]))
        ->toThrow(TicketOperationException::class, 'risk_sold_out');

    expect(fn () => app(RiskPoolService::class)->acquire($draw->id, null, [['number_4d' => '2468', 'amount' => 1]]))
        ->toThrow(TicketOperationException::class, 'risk_sold_out');
});

test('admin can recover a manually closed risk pool number', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-006',
        'business_date' => '2026-05-12',
        'sequence_no' => 6,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '2468',
        'total_cap_amount' => 1_000,
        'locked_amount' => 300,
        'remaining_amount' => 700,
        'sold_out_status' => 1,
        'version' => 2,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools/2468/recover')
        ->assertOk()
        ->assertJsonPath('data.normalized_number', '2468')
        ->assertJsonPath('data.is_sold_out', false)
        ->assertJsonPath('data.version', 3);

    $this->assertDatabaseHas('risk_pool_lock_logs', [
        'draw_id' => $draw->id,
        'normalized_number' => '2468',
        'action_type' => 'recover',
        'amount' => 0,
        'source_reason' => 'admin_manual_recover',
    ]);

    expect(app(RiskPoolService::class)->acquire($draw->id, null, [['number_4d' => '2468', 'amount' => 1]]))
        ->toBe(1);
});

test('admin risk pool lock logs include ticket_no when linked', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-002',
        'business_date' => '2026-05-12',
        'sequence_no' => 2,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPoolLockLog::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '5678',
        'ticket_item_id' => null,
        'action_type' => 'lock',
        'amount' => 50,
        'source_reason' => 'ticket_place',
        'created_at' => now(),
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pool-lock-logs?group_by=entry')
        ->assertOk()
        ->assertJsonPath('data.group_by', 'entry')
        ->assertJsonPath('data.meta.total', 1)
        ->assertJsonPath('data.items.0.amount', 50);
});

test('admin risk pool lock logs can group by ticket and filter active pools', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-007',
        'business_date' => '2026-05-12',
        'sequence_no' => 7,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '0043',
        'total_cap_amount' => 1_000_000,
        'locked_amount' => 0,
        'remaining_amount' => 1_000_000,
        'sold_out_status' => 0,
        'version' => 0,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '1288',
        'total_cap_amount' => 1_000,
        'locked_amount' => 850,
        'remaining_amount' => 150,
        'sold_out_status' => 0,
        'version' => 1,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools?active_only=1')
        ->assertOk()
        ->assertJsonPath('data.meta.total', 1)
        ->assertJsonPath('data.items.0.normalized_number', '1288');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pool-lock-logs?group_by=entry')
        ->assertOk()
        ->assertJsonPath('data.group_by', 'entry');
});

test('admin risk pool show 404 when pool missing', function (): void {
    $draw = Draw::query()->create([
        'draw_no' => '20260512-003',
        'business_date' => '2026-05-12',
        'sequence_no' => 3,
        'status' => 'open',
        'start_time' => now()->subHour(),
        'close_time' => now()->addHour(),
        'draw_time' => now()->addHours(2),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 0,
        'settle_version' => 0,
        'is_reopened' => false,
    ]);

    $token = mintRiskAdminToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/draws/'.$draw->id.'/risk-pools/0000')
        ->assertStatus(404);
});
