<?php

namespace App\Services;

use App\Models\User;
use App\Models\Wallet;
use App\Models\WalletTransfer;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use RuntimeException;

class WalletTransferService
{
    public function __construct(private WalletService $wallets) {}

    public function transfer(User $sender, string $receiverPublicId, int $amountMinor, ?string $note = null): WalletTransfer
    {
        if ($amountMinor <= 0) {
            throw new RuntimeException('Transfer amount must be positive.');
        }

        $receiver = User::where('public_id', $receiverPublicId)->where('status', 'active')->first();

        if (! $receiver) {
            throw new RuntimeException('Receiver was not found.');
        }

        if ($receiver->is($sender)) {
            throw new RuntimeException('You cannot transfer to yourself.');
        }

        return DB::transaction(function () use ($sender, $receiver, $amountMinor, $note) {
            $senderWallet = $this->wallets->ensureWallet($sender, 'EGP');
            $receiverWallet = $this->wallets->ensureWallet($receiver, 'EGP');

            Wallet::whereKey([$senderWallet->id, $receiverWallet->id])->orderBy('id')->lockForUpdate()->get();
            $senderWallet->refresh();
            $receiverWallet->refresh();

            $transfer = WalletTransfer::create([
                'uuid' => (string) Str::uuid(),
                'sender_user_id' => $sender->id,
                'receiver_user_id' => $receiver->id,
                'currency_code' => 'EGP',
                'amount_minor' => $amountMinor,
                'status' => 'completed',
                'note' => $note,
            ]);

            $this->wallets->debit(
                $senderWallet,
                $amountMinor,
                'transfer_out',
                'Wallet transfer to #'.$receiver->public_id,
                WalletTransfer::class,
                $transfer->id,
                ['receiver_public_id' => $receiver->public_id],
            );

            $this->wallets->credit(
                $receiverWallet,
                $amountMinor,
                'transfer_in',
                'Wallet transfer from #'.$sender->public_id,
                WalletTransfer::class,
                $transfer->id,
                ['sender_public_id' => $sender->public_id],
            );

            return $transfer;
        });
    }
}
