<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\User;
use App\Models\WalletTransaction;
use InvalidArgumentException;
use Illuminate\Support\Facades\DB;

class WalletService
{
    /**
     * Credit user wallet and create a completed ledger row.
     *
     * @param  array<string, mixed>  $meta
     */
    public function credit(
        User $user,
        int $amount,
        string $type,
        ?string $description = null,
        array $meta = [],
        ?int $paymentId = null,
        ?int $projectId = null,
        ?int $invoiceId = null,
        ?string $trackingCode = null,
    ): WalletTransaction {
        if ($amount <= 0) {
            throw new InvalidArgumentException('مبلغ واریز باید بزرگ‌تر از صفر باشد.');
        }

        return DB::transaction(function () use ($user, $amount, $type, $description, $meta, $paymentId, $projectId, $invoiceId, $trackingCode) {
            $locked = User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();
            $locked->increment('wallet', $amount);
            $balance = (int) $locked->fresh()->wallet;

            return WalletTransaction::create([
                'user_id' => $locked->id,
                'type' => $type,
                'amount' => $amount,
                'balance_after' => $balance,
                'description' => $description,
                'tracking_code' => $trackingCode,
                'status' => WalletTransaction::STATUS_COMPLETED,
                'payment_id' => $paymentId,
                'project_id' => $projectId,
                'invoice_id' => $invoiceId,
                'meta' => $meta ?: null,
            ]);
        });
    }

    /**
     * Debit user wallet and create a completed ledger row.
     *
     * @param  array<string, mixed>  $meta
     */
    public function debit(
        User $user,
        int $amount,
        string $type,
        ?string $description = null,
        array $meta = [],
        ?int $paymentId = null,
        ?int $projectId = null,
        ?int $invoiceId = null,
        ?string $trackingCode = null,
    ): WalletTransaction {
        if ($amount <= 0) {
            throw new InvalidArgumentException('مبلغ برداشت باید بزرگ‌تر از صفر باشد.');
        }

        return DB::transaction(function () use ($user, $amount, $type, $description, $meta, $paymentId, $projectId, $invoiceId, $trackingCode) {
            $locked = User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();

            if ((int) $locked->wallet < $amount) {
                throw new InvalidArgumentException('موجودی کیف پول کافی نیست.');
            }

            $locked->decrement('wallet', $amount);
            $balance = (int) $locked->fresh()->wallet;

            return WalletTransaction::create([
                'user_id' => $locked->id,
                'type' => $type,
                'amount' => $amount,
                'balance_after' => $balance,
                'description' => $description,
                'tracking_code' => $trackingCode,
                'status' => WalletTransaction::STATUS_COMPLETED,
                'payment_id' => $paymentId,
                'project_id' => $projectId,
                'invoice_id' => $invoiceId,
                'meta' => $meta ?: null,
            ]);
        });
    }

    /**
     * Hold funds for a pending withdrawal (debit immediately as pending withdrawal).
     */
    public function holdForWithdrawal(User $user, int $amount, ?string $description = null, array $meta = []): WalletTransaction
    {
        return $this->debit(
            $user,
            $amount,
            WalletTransaction::TYPE_WITHDRAWAL,
            $description ?? 'مسدودسازی مبلغ درخواست برداشت',
            array_merge($meta, ['hold' => true]),
        );
    }

    /**
     * Refund a previously held withdrawal amount.
     */
    public function releaseWithdrawalHold(User $user, int $amount, ?string $description = null, array $meta = []): WalletTransaction
    {
        return $this->credit(
            $user,
            $amount,
            WalletTransaction::TYPE_DEPOSIT,
            $description ?? 'بازگشت مبلغ درخواست برداشت رد شده',
            array_merge($meta, ['hold_release' => true]),
        );
    }
}
