<?php

namespace App\Services;

use App\Models\Transaction;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use InvalidArgumentException;

class TicketPurchaseService
{
    public function prepareForTilopay(Transaction $transaction, User $user): Transaction
    {
        return DB::transaction(function () use ($transaction, $user) {
            $transaction = Transaction::query()
                ->whereKey($transaction->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($transaction->user_id !== $user->id) {
                throw new InvalidArgumentException('La transacción no pertenece al usuario autenticado.');
            }

            if ($transaction->type !== 'purchase') {
                throw new InvalidArgumentException('La transacción no corresponde a una compra.');
            }

            if ($transaction->status !== 'pending') {
                throw new InvalidArgumentException('La transacción ya fue procesada.');
            }

            if ($transaction->provider && $transaction->provider !== 'tilopay') {
                throw new InvalidArgumentException('La transacción ya está asociada a otro proveedor de pago.');
            }

            if (!$transaction->raffle_id || !$transaction->quantity || $transaction->quantity <= 0) {
                throw new InvalidArgumentException('La transacción no tiene llaves asociadas.');
            }

            $transaction->update([
                'payment_method' => 'card',
                'provider' => 'tilopay',
                'provider_order_number' => $transaction->provider_order_number ?: $this->orderNumber(),
            ]);

            return $transaction->fresh(['user']);
        });
    }

    public function approve(Transaction $transaction, array $payload = []): Transaction
    {
        return DB::transaction(function () use ($transaction, $payload) {
            $transaction = Transaction::query()
                ->whereKey($transaction->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($transaction->status === 'approved') {
                return $transaction;
            }

            if ($transaction->status !== 'pending') {
                throw new InvalidArgumentException('La transacción no está pendiente.');
            }

            if ($transaction->provider !== 'tilopay') {
                throw new InvalidArgumentException('La transacción no corresponde a un pago por Tilopay.');
            }

            if (! in_array($transaction->type, ['purchase', 'deposit'], true)) {
                throw new InvalidArgumentException('Tipo de transacción no soportado para Tilopay.');
            }

            $transaction->update([
                'status' => 'approved',
                'payment_reference' => $payload['auth'] ?? $transaction->payment_reference,
                'provider_transaction_id' => $payload['tilopay-transaction'] ?? $payload['tpt'] ?? $transaction->provider_transaction_id,
                'provider_payload' => $payload ?: $transaction->provider_payload,
                'approved_at' => now(),
            ]);

            return $transaction->fresh(['user']);
        });
    }

    public function reject(Transaction $transaction, array $payload = []): Transaction
    {
        return DB::transaction(function () use ($transaction, $payload) {
            $transaction = Transaction::query()
                ->whereKey($transaction->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($transaction->status === 'approved' || $transaction->status === 'rejected') {
                return $transaction;
            }

            $transaction->update([
                'status' => 'rejected',
                'payment_reference' => $payload['auth'] ?? $transaction->payment_reference,
                'provider_transaction_id' => $payload['tilopay-transaction'] ?? $payload['tpt'] ?? $transaction->provider_transaction_id,
                'provider_payload' => $payload ?: $transaction->provider_payload,
                'rejected_at' => now(),
            ]);

            return $transaction;
        });
    }

    public function releaseExpiredReservations(?int $raffleId = null): int
    {
        // El sistema de llaves no requiere expiración de boletos apartados antes de compra
        return 0;
    }

    private function orderNumber(): string
    {
        return 'WINCAR-'.now()->format('YmdHis').'-'.Str::upper(Str::random(8));
    }
}
