<?php

namespace App\Livewire\Billing;

use App\Actions\VerifyPayment;
use App\Enums\PaymentGateway;
use App\Enums\ProofStatus;
use App\Livewire\Concerns\InteractsWithToasts;
use App\Livewire\Concerns\WithDataTable;
use App\Models\SubscriptionPayment;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;
use Throwable;

/**
 * The verification queue — the reason this panel exists.
 *
 * A shop owner sends money over Easypaisa, JazzCash or IBFT, uploads the
 * screenshot, and then waits. Nothing about that transfer reaches us
 * automatically, so a human compares the screenshot against the bank
 * statement and decides. Until they do, the shop cannot trade.
 *
 * The screen is therefore built for speed and for care in equal measure:
 * one payment at a time, everything needed to judge it on one page, and a
 * mandatory reason on every refusal.
 */
#[Title('Verification queue')]
class PaymentQueue extends Component
{
    use InteractsWithToasts, WithDataTable;

    #[Url(except: 'pending')]
    public string $status = 'pending';

    #[Url(except: '')]
    public string $gateway = '';

    public ?int $selectedId = null;

    public string $note = '';

    public string $rejectionReason = '';

    public bool $showReject = false;

    public bool $showReopen = false;

    public string $reopenReason = '';

    public function mount(): void
    {
        abort_unless(auth()->user()?->canVerifyPayments(), 403);
    }

    protected function searchableColumns(): array
    {
        return ['reference', 'transaction_id', 'sender_name', 'sender_account', 'tenant.business_name', 'tenant.owner_phone'];
    }

    protected function defaultSort(): array
    {
        // Oldest first: whoever has waited longest gets answered first.
        return ['created_at', 'asc'];
    }

    protected function sortableColumns(): array
    {
        return ['created_at', 'amount', 'reference', 'status'];
    }

    protected function filterProperties(): array
    {
        return ['status', 'gateway'];
    }

    public function updatedStatus(): void
    {
        $this->resetPage();
        $this->selectedId = null;
    }

    public function updatedGateway(): void
    {
        $this->resetPage();
        $this->selectedId = null;
    }

    protected function baseQuery(): Builder
    {
        return SubscriptionPayment::query()
            ->with(['tenant.product', 'subscription.plan', 'invoice', 'account']);
    }

    protected function applyFilters(Builder $query): Builder
    {
        if ($this->status !== '' && $this->status !== 'all') {
            $query->where('status', $this->status);
        }

        if ($this->gateway !== '') {
            $query->where('gateway', $this->gateway);
        }

        return $query;
    }

    public function rows()
    {
        return $this->applyDataTable($this->applyFilters($this->baseQuery()))
            ->paginate($this->perPage);
    }

    public function selected(): ?SubscriptionPayment
    {
        if (! $this->selectedId) {
            return null;
        }

        return SubscriptionPayment::with(['tenant.product', 'subscription.plan', 'invoice', 'account'])
            ->find($this->selectedId);
    }

    public function select(int $id): void
    {
        $this->selectedId = $id;
        $this->note = '';
        $this->rejectionReason = '';
        $this->showReject = false;
        $this->showReopen = false;
        $this->resetValidation();
    }

    public function clearSelection(): void
    {
        $this->selectedId = null;
    }

    /**
     * Other payments quoting the same transaction ID.
     *
     * The commonest fraud on manual rails is submitting one screenshot twice,
     * so the panel looks for it rather than trusting the reviewer to remember.
     */
    public function duplicatesFor(SubscriptionPayment $payment)
    {
        if (! $payment->transaction_id) {
            return collect();
        }

        return SubscriptionPayment::query()
            ->with('tenant')
            ->where('transaction_id', $payment->transaction_id)
            ->whereKeyNot($payment->id)
            ->latest('id')
            ->limit(5)
            ->get();
    }

    /*
    |--------------------------------------------------------------------------
    | Decisions
    |--------------------------------------------------------------------------
    */

    public function approve(): void
    {
        $payment = $this->selected();

        if (! $payment) {
            return;
        }

        try {
            app(VerifyPayment::class)->approve($payment, $this->note ?: null);

            $this->toastSuccess('Payment verified — the subscription is live.');
            $this->afterDecision();
        } catch (Throwable $exception) {
            $this->toastError($exception->getMessage());
        }
    }

    public function openReject(): void
    {
        $this->showReject = true;
    }

    public function reject(): void
    {
        $this->validate(
            ['rejectionReason' => 'required|string|min:5|max:500'],
            ['rejectionReason.required' => 'Write what is wrong — the shop owner reads this.'],
            ['rejectionReason' => 'reason'],
        );

        $payment = $this->selected();

        if (! $payment) {
            return;
        }

        try {
            app(VerifyPayment::class)->reject($payment, $this->rejectionReason, $this->note ?: null);

            $this->toastWarning('Payment rejected and the reason sent back.');
            $this->showReject = false;
            $this->afterDecision();
        } catch (Throwable $exception) {
            $this->toastError($exception->getMessage());
        }
    }

    public function openReopen(): void
    {
        $this->showReopen = true;
    }

    public function reopen(): void
    {
        $this->validate(
            ['reopenReason' => 'required|string|min:5|max:500'],
            [],
            ['reopenReason' => 'reason'],
        );

        $payment = $this->selected();

        if (! $payment) {
            return;
        }

        try {
            app(VerifyPayment::class)->reopen($payment, $this->reopenReason);

            $this->toastSuccess('Reopened. It is back in the pending queue.');
            $this->showReopen = false;
            $this->reopenReason = '';
        } catch (Throwable $exception) {
            $this->toastError($exception->getMessage());
        }
    }

    /**
     * Move straight to the next waiting payment.
     *
     * Verification is done in batches; making the reviewer hunt for the next
     * row after every decision is how backlogs form.
     */
    protected function afterDecision(): void
    {
        $this->note = '';
        $this->rejectionReason = '';

        $next = SubscriptionPayment::query()
            ->where('status', ProofStatus::Pending)
            ->whereKeyNot($this->selectedId)
            ->oldest('created_at')
            ->first();

        $this->selectedId = $next?->id;
    }

    public function render()
    {
        $payment = $this->selected();

        return view('livewire.billing.payment-queue', [
            'rows' => $this->rows(),
            'payment' => $payment,
            'duplicates' => $payment ? $this->duplicatesFor($payment) : collect(),
            'statuses' => ['pending' => 'Pending', 'verified' => 'Verified', 'rejected' => 'Rejected', 'all' => 'All'],
            'gateways' => PaymentGateway::options(),
            'pendingCount' => SubscriptionPayment::where('status', ProofStatus::Pending)->count(),
        ]);
    }
}
