<?php

namespace App\Services\Settlement;

use App\Models\Player;
use App\Models\AdminUser;
use App\Services\AuditLogger;
use Illuminate\Http\Request;
use App\Models\SettlementBatch;
use App\Models\TicketSettlementDetail;
use Illuminate\Support\Facades\DB;
use App\Services\Ticket\TicketWalletService;
use App\Http\Middleware\RecordAdminApiAudit;
use App\Lottery\SettlementBatchStatus;

final class SettlementPayoutCorrectionService
{
    public function __construct(
        private readonly TicketWalletService $walletService,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function apply(
        SettlementBatch $batch,
        AdminUser $admin,
        int $playerId,
        int $amountDelta,
        string $reason,
        Request $request,
    ): array {
        return DB::transaction(function () use ($batch, $admin, $playerId, $amountDelta, $reason, $request): array {
            /** @var SettlementBatch $lockedBatch */
            $lockedBatch = SettlementBatch::query()->whereKey($batch->id)->lockForUpdate()->firstOrFail();
            if ($lockedBatch->status !== SettlementBatchStatus::Paid->value) {
                throw new \RuntimeException('settlement_batch_not_paid');
            }

            $detail = TicketSettlementDetail::query()
                ->where('settlement_batch_id', $lockedBatch->id)
                ->whereHas('ticketItem', fn ($query) => $query->where('player_id', $playerId))
                ->with(['ticketItem.order'])
                ->orderBy('id')
                ->first();

            if ($detail === null || $detail->ticketItem === null) {
                throw new \RuntimeException('settlement_player_not_in_batch');
            }

            $player = Player::query()->whereKey($playerId)->firstOrFail();
            $currencyCode = strtoupper((string) ($detail->ticketItem->order?->currency_code ?? 'NPR'));
            $correctionNo = $this->newCorrectionNo();
            $remark = sprintf(
                'settlement_batch_adjustment:%d:%d:%s',
                (int) $lockedBatch->id,
                $playerId,
                mb_substr(trim($reason), 0, 180)
            );

            $before = [
                'batch_id' => (int) $lockedBatch->id,
                'batch_status' => (string) $lockedBatch->status,
                'player_id' => $playerId,
                'currency_code' => $currencyCode,
                'amount_delta' => $amountDelta,
                'reason' => $reason,
                'ticket_item_id' => (int) $detail->ticket_item_id,
                'ticket_no' => (string) ($detail->ticketItem->ticket_no ?? ''),
                'original_win_amount' => (int) $detail->win_amount,
                'original_jackpot_allocation_amount' => (int) $detail->jackpot_allocation_amount,
            ];

            $txnNo = $this->walletService->applySettlementCorrection(
                $player,
                $currencyCode,
                $amountDelta,
                $correctionNo,
                $remark,
            );

            AuditLogger::recordForAdmin(
                $admin,
                $request,
                moduleCode: 'settlement',
                actionCode: 'payout_adjustment',
                targetType: 'settlement_batch_adjustment',
                targetId: $correctionNo,
                beforeJson: $before,
                afterJson: [
                    'batch_id' => (int) $lockedBatch->id,
                    'player_id' => $playerId,
                    'currency_code' => $currencyCode,
                    'amount_delta' => $amountDelta,
                    'direction' => $amountDelta > 0 ? 'credit' : 'debit',
                    'reason' => $reason,
                    'correction_no' => $correctionNo,
                    'wallet_txn_no' => $txnNo,
                ],
            );
            $request->attributes->set(RecordAdminApiAudit::ATTRIBUTE_AUDIT_RECORDED, true);

            return [
                'batch_id' => (int) $lockedBatch->id,
                'player_id' => $playerId,
                'currency_code' => $currencyCode,
                'amount_delta' => $amountDelta,
                'direction' => $amountDelta > 0 ? 'credit' : 'debit',
                'reason' => $reason,
                'correction_no' => $correctionNo,
                'wallet_txn_no' => $txnNo,
            ];
        });
    }

    private function newCorrectionNo(): string
    {
        return 'SC'.now()->format('YmdHis').str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
    }
}
