<?php

use App\Models\Player;
use App\Models\WalletTxn;
use App\Lottery\ErrorCode;
use App\Models\PlayerWallet;
use App\Models\TransferOrder;
use App\Services\LotterySettings;
use Database\Seeders\CurrencySeeder;
use Illuminate\Support\Facades\Http;
use Database\Seeders\LotterySettingsSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    config(['lottery.main_site.wallet_api_url' => null]);
    $this->seed(CurrencySeeder::class);
    $this->seed(LotterySettingsSeeder::class);
});

test('transfer in credits lottery wallet via stub main site', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u1',
        'username' => 'a',
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $key = 'idem-'.uniqid('', true);

    $response = $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 500,
            'currency' => 'NPR',
            'idempotent_key' => $key,
        ]);
    $response->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonPath('data.amount', 500)
        ->assertJsonPath('data.currency_code', 'NPR')
        ->assertJsonPath('data.balance', 500)
        ->assertJsonPath('data.lottery_balance_after', 500);

    expect((string) $response->json('data.log_id'))->toStartWith('WX_');

    $wallet = PlayerWallet::query()->where('player_id', $player->id)->first();
    expect($wallet)->not->toBeNull()
        ->and((int) $wallet->balance)->toBe(500);

    $order = TransferOrder::query()->where('idempotent_key', $key)->first();
    expect($order?->status)->toBe('success')
        ->and($order->external_request_payload)->not->toBeNull()
        ->and($order->external_request_payload['amount_minor'])->toBe(500)
        ->and($order->external_request_payload['_meta']['stub'] ?? null)->toBeTrue();
    expect(WalletTxn::query()->where('biz_type', 'transfer_in')->count())->toBe(1);
});

test('transfer in idempotent replay returns same success shape', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u2',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $key = 'idem-replay-1';

    $first = $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 100,
            'idempotent_key' => $key,
        ]);

    $second = $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 100,
            'idempotent_key' => $key,
        ]);

    $first->assertOk();
    $second->assertOk();

    expect((string) $first->json('data.transfer_no'))->toBe((string) $second->json('data.transfer_no'))
        ->and(PlayerWallet::query()->where('player_id', $player->id)->first()?->balance)->toBe(100);
});

test('transfer out debits lottery and matches stub credit', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u3',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    PlayerWallet::query()->create([
        'player_id' => $player->id,
        'wallet_type' => 'lottery',
        'currency_code' => 'NPR',
        'balance' => 1000,
        'frozen_balance' => 0,
        'status' => 0,
        'version' => 0,
    ]);

    $key = 'idem-out-1';

    $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-out', [
            'amount' => 400,
            'idempotent_key' => $key,
        ])
        ->assertOk()
        ->assertJsonPath('data.lottery_balance_after', 600);

    expect((int) PlayerWallet::query()->where('player_id', $player->id)->first()?->balance)->toBe(600);
});

test('transfer out respects frozen balance when checking available funds', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u-frozen',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    PlayerWallet::query()->create([
        'player_id' => $player->id,
        'wallet_type' => 'lottery',
        'currency_code' => 'NPR',
        'balance' => 10_000,
        'frozen_balance' => 9_500,
        'status' => 0,
        'version' => 0,
    ]);

    $code = ErrorCode::WalletInsufficientBalance->value;

    $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-out', [
            'amount' => 1_000,
            'idempotent_key' => 'idem-out-frozen-too-much',
        ])
        ->assertStatus(400)
        ->assertJsonPath('code', $code);

    $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-out', [
            'amount' => 400,
            'idempotent_key' => 'idem-out-frozen-ok',
        ])
        ->assertOk()
        ->assertJsonPath('data.lottery_balance_after', 9_600);

    expect((int) PlayerWallet::query()->where('player_id', $player->id)->value('balance'))->toBe(9_600)
        ->and((int) PlayerWallet::query()->where('player_id', $player->id)->value('frozen_balance'))->toBe(9_500);
});

test('transfer out insufficient balance fails with 1001', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u4',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $key = 'idem-out-broke';

    $code = ErrorCode::WalletInsufficientBalance->value;

    $this->withHeaders([
        'Authorization' => 'Bearer dev:'.$player->id,
        'X-Locale' => 'zh',
    ])
        ->postJson('/api/v1/wallet/transfer-out', [
            'amount' => 100,
            'idempotent_key' => $key,
        ])
        ->assertStatus(400)
        ->assertJsonPath('code', $code)
        ->assertJsonPath('msg', __("wallet.$code", [], 'zh'));

    $this->withHeaders([
        'Authorization' => 'Bearer dev:'.$player->id,
        'X-Locale' => 'zh',
    ])
        ->postJson('/api/v1/wallet/transfer-out', [
            'amount' => 100,
            'idempotent_key' => $key,
        ])
        ->assertStatus(400)
        ->assertJsonPath('code', $code);
});

test('transfer in disabled returns 1004', function () {
    LotterySettings::put('wallet.transfer_in_enabled', false, 'wallet');

    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u5',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $code = ErrorCode::WalletTransferInDisabled->value;

    $this->withHeaders([
        'Authorization' => 'Bearer dev:'.$player->id,
        'Accept-Language' => 'en',
    ])
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 10,
            'idempotent_key' => 'disabled-test',
        ])
        ->assertStatus(400)
        ->assertJsonPath('code', $code);
});

test('transfer in below min amount returns 1003', function () {
    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u-min',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $code = ErrorCode::WalletAmountExceedsLimit->value;

    $this->withHeaders([
        'Authorization' => 'Bearer dev:'.$player->id,
        'X-Locale' => 'zh',
    ])
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 50,
            'idempotent_key' => 'below-min',
        ])
        ->assertStatus(400)
        ->assertJsonPath('code', $code);
});

test('transfer in http 504 marks order pending reconcile', function () {
    Http::fake([
        'fake-main.test/*' => Http::response([], 504),
    ]);
    config(['lottery.main_site.wallet_api_url' => 'https://fake-main.test']);
    config(['lottery.main_site.wallet_debit_path' => 'debit']);

    $player = Player::query()->create([
        'site_code' => 'main',
        'site_player_id' => 'u504',
        'username' => null,
        'nickname' => null,
        'default_currency' => 'NPR',
        'status' => 0,
    ]);

    $key = 'idem-504-'.uniqid('', true);

    $this->withHeader('Authorization', 'Bearer dev:'.$player->id)
        ->postJson('/api/v1/wallet/transfer-in', [
            'amount' => 500,
            'currency' => 'NPR',
            'idempotent_key' => $key,
        ])
        ->assertStatus(409)
        ->assertJsonPath('code', ErrorCode::WalletTransferPending->value);

    $order = TransferOrder::query()->where('idempotent_key', $key)->first();
    expect($order?->status)->toBe('pending_reconcile')
        ->and($order->external_request_payload['currency_code'])->toBe('NPR')
        ->and($order->external_request_payload['_meta']['path'] ?? null)->toBe('/debit');
    expect(WalletTxn::query()->where('player_id', $player->id)->count())->toBe(0);
});
