<?php

namespace App\Services\Jackpot;

use App\Models\AdminUser;
use App\Models\JackpotPool;
use App\Models\JackpotPoolAdjustment;
use App\Services\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

final class JackpotPoolAdjustmentService
{
    public function apply(
        JackpotPool $pool,
        AdminUser $admin,
        int $amountDelta,
        string $reason,
        ?Request $request = null,
    ): JackpotPoolAdjustment {
        if ($amountDelta === 0) {
            throw new \RuntimeException('adjustment_delta_zero');
        }

        $reason = trim($reason);
        if ($reason === '') {
            throw new \RuntimeException('adjustment_reason_required');
        }

        return DB::transaction(function () use ($pool, $admin, $amountDelta, $reason, $request): JackpotPoolAdjustment {
            /** @var JackpotPool $locked */
            $locked = JackpotPool::query()->whereKey($pool->id)->lockForUpdate()->firstOrFail();

            $before = (int) $locked->current_amount;
            $after = $before + $amountDelta;
            if ($after < 0) {
                throw new \RuntimeException('adjustment_would_make_balance_negative');
            }

            $adjustment = JackpotPoolAdjustment::query()->create([
                'adjustment_no' => $this->newAdjustmentNo(),
                'jackpot_pool_id' => $locked->id,
                'admin_user_id' => $admin->id,
                'amount_delta' => $amountDelta,
                'balance_before' => $before,
                'balance_after' => $after,
                'reason' => $reason,
            ]);

            $locked->forceFill(['current_amount' => $after])->save();

            $snapshot = [
                'currency_code' => $locked->currency_code,
                'amount_delta' => $amountDelta,
                'balance_before' => $before,
                'balance_after' => $after,
                'reason' => $reason,
                'adjustment_no' => $adjustment->adjustment_no,
            ];

            if ($request !== null) {
                AuditLogger::recordFromRequest(
                    $request,
                    AuditLogger::OPERATOR_ADMIN,
                    $admin->id,
                    'jackpot',
                    'adjust_balance',
                    'jackpot_pool',
                    (string) $locked->id,
                    ['current_amount' => $before],
                    $snapshot,
                );
            } else {
                AuditLogger::record(
                    AuditLogger::OPERATOR_ADMIN,
                    $admin->id,
                    'jackpot',
                    'adjust_balance',
                    'jackpot_pool',
                    (string) $locked->id,
                    ['current_amount' => $before],
                    $snapshot,
                );
            }

            return $adjustment->fresh(['adminUser']);
        });
    }

    private function newAdjustmentNo(): string
    {
        return 'JA'.now()->format('YmdHis').Str::upper(Str::random(6));
    }
}
