<?php

use App\Models\PlayType;
use App\Models\AdminUser;
use App\Models\OddsVersion;
use App\Models\RiskCapVersion;
use App\Models\PlayConfigVersion;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use Database\Seeders\CurrencySeeder;
use Database\Seeders\PlayTypeSeeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Database\Seeders\OperationalConfigV1Seeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    $this->seed(CurrencySeeder::class);
    $this->seed(PlayTypeSeeder::class);
    $this->seed(OperationalConfigV1Seeder::class);
});

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

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

function oddsPutPayloadFromDetail(array $items): array
{
    return collect($items)->map(fn (array $r) => [
        'play_code' => $r['play_code'],
        'prize_scope' => $r['prize_scope'],
        'odds_value' => (int) $r['odds_value'],
        'rebate_rate' => (float) $r['rebate_rate'],
        'commission_rate' => (float) $r['commission_rate'],
        'currency_code' => $r['currency_code'],
        'extra_config_json' => $r['extra_config_json'] ?? null,
    ])->all();
}

test('play effective catalog is public and merged', function (): void {
    $resp = $this->getJson('/api/v1/play/effective?currency=NPR');
    $resp->assertOk()->assertJsonPath('data.currency_code', 'NPR');

    $plays = $resp->json('data.plays');
    expect($plays)->toBeArray()->not->toBeEmpty();
    expect($plays[0])->toHaveKeys(['play_code', 'config', 'odds', 'master_enabled']);
});

test('admin play config draft publish flow', function (): void {
    $token = mintConfigAdminToken();
    $active = PlayConfigVersion::query()->where('status', ConfigVersionStatus::Active->value)->firstOrFail();

    $create = $this->postJson('/api/v1/admin/config/play-versions', [
        'reason' => 'test draft',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');
    expect($draftId)->toBeGreaterThan(0);
    expect($create->json('data.status'))->toBe(ConfigVersionStatus::Draft->value);

    $types = PlayType::query()->orderBy('play_code')->get();
    $itemPayload = [];
    foreach ($types as $t) {
        $itemPayload[] = [
            'play_code' => $t->play_code,
            'category' => $t->category,
            'dimension' => $t->dimension,
            'bet_mode' => $t->bet_mode,
            'display_name' => $t->display_name ?? $t->play_code,
            'is_enabled' => true,
            'min_bet_amount' => 200,
            'max_bet_amount' => 400_000_000,
            'display_order' => (int) $t->sort_order,
            'supports_multi_number' => (bool) $t->supports_multi_number,
            'reserved_rule_json' => $t->reserved_rule_json,
        ];
    }

    $this->putJson(
        '/api/v1/admin/config/play-versions/'.$draftId.'/items',
        ['items' => $itemPayload],
        ['Authorization' => 'Bearer '.$token],
    )->assertOk()->assertJsonPath('data.items.0.min_bet_amount', 200);

    $this->postJson(
        '/api/v1/admin/config/play-versions/'.$draftId.'/publish',
        [],
        ['Authorization' => 'Bearer '.$token],
    )->assertOk()->assertJsonPath('data.status', ConfigVersionStatus::Active->value);

    $active->refresh();
    expect($active->status)->toBe(ConfigVersionStatus::Archived->value);

    expect(PlayConfigVersion::query()->where('status', ConfigVersionStatus::Active->value)->count())->toBe(1);
});

test('admin play config publish rejects empty draft items', function (): void {
    $token = mintConfigAdminToken();
    $create = $this->postJson('/api/v1/admin/config/play-versions', [
        'reason' => 'empty draft',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');

    DB::table('play_config_items')->where('version_id', $draftId)->delete();

    $this->postJson(
        '/api/v1/admin/config/play-versions/'.$draftId.'/publish',
        [],
        ['Authorization' => 'Bearer '.$token],
    )->assertStatus(422);
});

test('admin play-types requires authentication', function (): void {
    $this->getJson('/api/v1/admin/play-types')->assertUnauthorized();
});

test('admin cannot delete active play config version', function (): void {
    $token = mintConfigAdminToken();
    $active = PlayConfigVersion::query()->where('status', ConfigVersionStatus::Active->value)->firstOrFail();

    $this->deleteJson('/api/v1/admin/config/play-versions/'.$active->id, [], [
        'Authorization' => 'Bearer '.$token,
    ])
        ->assertStatus(400)
        ->assertJsonPath('code', ErrorCode::ConfigVersionCannotDeleteActive->value);
});

test('admin can delete draft play config version', function (): void {
    $token = mintConfigAdminToken();
    $create = $this->postJson('/api/v1/admin/config/play-versions', [
        'reason' => 'to delete',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');

    $this->deleteJson('/api/v1/admin/config/play-versions/'.$draftId, [], [
        'Authorization' => 'Bearer '.$token,
    ])
        ->assertOk()
        ->assertJsonPath('data.deleted', true);

    expect(PlayConfigVersion::query()->whereKey($draftId)->exists())->toBeFalse();
});

test('admin can delete draft odds version', function (): void {
    $token = mintConfigAdminToken();
    $create = $this->postJson('/api/v1/admin/config/odds-versions', [
        'reason' => 'to delete',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');

    $this->deleteJson('/api/v1/admin/config/odds-versions/'.$draftId, [], [
        'Authorization' => 'Bearer '.$token,
    ])
        ->assertOk()
        ->assertJsonPath('data.deleted', true);

    expect(OddsVersion::query()->whereKey($draftId)->exists())->toBeFalse();
});

test('admin can delete draft risk cap version', function (): void {
    $token = mintConfigAdminToken();
    $create = $this->postJson('/api/v1/admin/config/risk-cap-versions', [
        'reason' => 'to delete',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');

    $this->deleteJson('/api/v1/admin/config/risk-cap-versions/'.$draftId, [], [
        'Authorization' => 'Bearer '.$token,
    ])
        ->assertOk()
        ->assertJsonPath('data.deleted', true);

    expect(RiskCapVersion::query()->whereKey($draftId)->exists())->toBeFalse();
});

test('admin odds config rejects out of range rebate rate', function (): void {
    $token = mintConfigAdminToken();
    $create = $this->postJson('/api/v1/admin/config/odds-versions', [
        'reason' => 'rate bound',
    ], ['Authorization' => 'Bearer '.$token]);
    $create->assertOk();
    $draftId = (int) $create->json('data.id');

    $detail = $this->getJson('/api/v1/admin/config/odds-versions/'.$draftId, [
        'Authorization' => 'Bearer '.$token,
    ])->assertOk()->json('data.items');
    $payload = oddsPutPayloadFromDetail($detail);
    $payload[0]['rebate_rate'] = 1.2;

    $this->putJson(
        '/api/v1/admin/config/odds-versions/'.$draftId.'/items',
        ['items' => $payload],
        ['Authorization' => 'Bearer '.$token],
    )->assertStatus(422);
});
