<?php

namespace App\Modules\Wallet\Repositories;

use App\Core\Repositories\BaseRepository;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Modules\Wallet\Interfaces\WalletRepositoryInterface;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;

class WalletRepository extends BaseRepository implements WalletRepositoryInterface
{
    public function __construct(Wallet $model)
    {
        parent::__construct($model);
    }

    public function findByUserId(string $userId): ?Wallet
    {
        return $this->model->where('user_id', $userId)->first();
    }

    public function createForUser(string $userId): Wallet
    {
        return $this->model->create([
            'user_id' => $userId,
            'balance' => 0,
        ]);
    }

    public function getBalance(string $walletId): float
    {
        $wallet = $this->findOrFail($walletId);
        return (float) $wallet->balance;
    }

    public function credit(
        string $walletId,
        float $amount,
        string $description,
        string $reference,
        ?string $relatedType = null,
        ?string $relatedId = null,
        ?array $metadata = null,
        bool $isRefund = false
    ): WalletTransaction {
        return DB::transaction(function () use ($walletId, $amount, $description, $reference, $relatedType, $relatedId, $metadata, $isRefund) {
            $wallet = $this->model->lockForUpdate()->findOrFail($walletId);
            $balanceBefore = (float) $wallet->balance;
            $balanceAfter = $balanceBefore + $amount;

            $wallet->update([
                'balance' => $balanceAfter,
                'total_funded' => $isRefund ? $wallet->total_funded : $wallet->total_funded + $amount,
                'total_spent' => $isRefund ? $wallet->total_spent - $amount : $wallet->total_spent,
            ]);

            return WalletTransaction::create([
                'wallet_id' => $walletId,
                'user_id' => $wallet->user_id,
                'type' => 'credit',
                'amount' => $amount,
                'balance_before' => $balanceBefore,
                'balance_after' => $balanceAfter,
                'description' => $description,
                'reference' => $reference,
                'related_type' => $relatedType,
                'related_id' => $relatedId,
                'status' => 'completed',
                'metadata' => $metadata,
            ]);
        });
    }

    public function debit(
        string $walletId,
        float $amount,
        string $description,
        string $reference,
        ?string $relatedType = null,
        ?string $relatedId = null,
        ?array $metadata = null
    ): WalletTransaction {
        return DB::transaction(function () use ($walletId, $amount, $description, $reference, $relatedType, $relatedId, $metadata) {
            $wallet = $this->model->lockForUpdate()->findOrFail($walletId);

            if ($wallet->status !== 'active' || (float) $wallet->balance < $amount) {
                throw new \App\Core\Exceptions\InsufficientBalanceException();
            }

            $balanceBefore = (float) $wallet->balance;
            $balanceAfter = $balanceBefore - $amount;

            $wallet->update([
                'balance' => $balanceAfter,
                'total_spent' => $wallet->total_spent + $amount,
            ]);

            return WalletTransaction::create([
                'wallet_id' => $walletId,
                'user_id' => $wallet->user_id,
                'type' => 'debit',
                'amount' => $amount,
                'balance_before' => $balanceBefore,
                'balance_after' => $balanceAfter,
                'description' => $description,
                'reference' => $reference,
                'related_type' => $relatedType,
                'related_id' => $relatedId,
                'status' => 'completed',
                'metadata' => $metadata,
            ]);
        });
    }

    public function canDebit(string $walletId, float $amount): bool
    {
        return DB::transaction(function () use ($walletId, $amount) {
            $wallet = $this->model->lockForUpdate()->findOrFail($walletId);
            return $wallet->status === 'active' && (float) $wallet->balance >= $amount;
        });
    }

    public function getTransactions(string $walletId, int $perPage = 15, ?string $type = null): LengthAwarePaginator
    {
        $query = WalletTransaction::where('wallet_id', $walletId)
            ->orderBy('created_at', 'desc');

        if ($type) {
            $query->where('type', $type);
        }

        return $query->paginate($perPage);
    }

    public function getStatement(string $walletId, ?string $fromDate = null, ?string $toDate = null): Collection
    {
        $query = WalletTransaction::where('wallet_id', $walletId)
            ->orderBy('created_at', 'asc');

        if ($fromDate) {
            $query->where('created_at', '>=', $fromDate);
        }

        if ($toDate) {
            $query->where('created_at', '<=', $toDate);
        }

        return $query->get();
    }
}
