<?php

namespace App\Services;

use App\Enums\WalletTransactionType;
use App\Models\User;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
use RuntimeException;

class WalletService
{
    public function createForUser(User $user): Wallet
    {
        return Wallet::query()->firstOrCreate(
            ['user_id' => $user->id],
            ['balance' => 0, 'is_active' => true],
        );
    }

    public function credit(
        Wallet $wallet,
        int $amount,
        ?string $description = null,
        ?User $performedBy = null,
    ): WalletTransaction {
        return $this->apply($wallet, WalletTransactionType::Credit, $amount, $description, $performedBy);
    }

    public function debit(
        Wallet $wallet,
        int $amount,
        ?string $description = null,
        ?User $performedBy = null,
    ): WalletTransaction {
        return $this->apply($wallet, WalletTransactionType::Debit, $amount, $description, $performedBy);
    }

    private function apply(
        Wallet $wallet,
        WalletTransactionType $type,
        int $amount,
        ?string $description,
        ?User $performedBy,
    ): WalletTransaction {
        if ($amount <= 0) {
            throw new InvalidArgumentException('مبلغ باید بزرگ‌تر از صفر باشد.');
        }

        return DB::transaction(function () use ($wallet, $type, $amount, $description, $performedBy): WalletTransaction {
            $wallet = Wallet::query()->lockForUpdate()->findOrFail($wallet->id);

            if (! $wallet->is_active) {
                throw new RuntimeException('کیف پول غیرفعال است.');
            }

            $balanceBefore = $wallet->balance;
            $balanceAfter = $type === WalletTransactionType::Credit
                ? $balanceBefore + $amount
                : $balanceBefore - $amount;

            if ($balanceAfter < 0) {
                throw new RuntimeException('موجودی کیف پول کافی نیست.');
            }

            $wallet->update(['balance' => $balanceAfter]);

            return WalletTransaction::query()->create([
                'wallet_id' => $wallet->id,
                'type' => $type,
                'amount' => $amount,
                'balance_before' => $balanceBefore,
                'balance_after' => $balanceAfter,
                'description' => $description,
                'created_by' => $performedBy?->id,
            ]);
        });
    }
}
