<?php

namespace App\Http\Controllers;

use App\Http\Requests\ReversePaymentRequest;
use App\Http\Requests\StorePaymentRequest;
use App\Http\Requests\UpdatePaymentRequest;
use App\Enums\PaymentMethod;
use App\Models\Invoice;
use App\Models\Payment;
use App\Services\PaymentService;
use App\Services\PdfService;
use App\Services\SettingsService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PaymentController extends Controller
{
    public function index(Request $request): View
    {
        $this->authorize('viewAny', Payment::class);

        $payments = Payment::query()
            ->with(['invoice', 'customer'])
            ->when($request->filled('search'), function ($query) use ($request): void {
                $search = $request->string('search')->toString();
                $query->where(function ($builder) use ($search): void {
                    $builder->where('payment_number', 'like', "%{$search}%")
                        ->orWhere('transaction_reference', 'like', "%{$search}%");
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', $request->string('status')))
            ->latest()
            ->paginate(15)
            ->withQueryString();

        return view('payments.index', compact('payments'));
    }

    public function create(Request $request): View
    {
        $this->authorize('create', Payment::class);

        $invoice = $request->filled('invoice_id')
            ? Invoice::query()->with('customer')->find($request->integer('invoice_id'))
            : null;

        $invoices = Invoice::query()
            ->with('customer')
            ->whereNotIn('status', ['draft', 'void'])
            ->where('outstanding_balance', '>', 0)
            ->latest()
            ->limit(100)
            ->get();

        $paymentMethods = collect(PaymentMethod::cases())
            ->mapWithKeys(fn (PaymentMethod $method) => [$method->value => $method->label()]);

        return view('payments.create', compact('invoice', 'invoices', 'paymentMethods'));
    }

    public function store(StorePaymentRequest $request, PaymentService $paymentService): RedirectResponse
    {
        $this->authorize('create', Payment::class);

        $invoice = Invoice::query()->findOrFail($request->validated('invoice_id'));

        try {
            $payment = $paymentService->record($invoice, $request->validated());
        } catch (\App\Exceptions\DocumentException $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        if ($request->boolean('return_to_invoice')) {
            return redirect()
                ->route('invoices.show', $invoice)
                ->with('success', 'Payment recorded successfully.');
        }

        return redirect()
            ->route('payments.show', $payment)
            ->with('success', 'Payment recorded successfully.');
    }

    public function show(Payment $payment): View
    {
        $this->authorize('view', $payment);

        $payment->load(['invoice.items', 'customer', 'receiver', 'attachments.uploader']);

        return view('payments.show', compact('payment'));
    }

    public function edit(Payment $payment): View
    {
        $this->authorize('update', $payment);

        $payment->load(['invoice.customer']);
        $paymentMethods = collect(PaymentMethod::cases())
            ->mapWithKeys(fn (PaymentMethod $method) => [$method->value => $method->label()]);

        return view('payments.edit', compact('payment', 'paymentMethods'));
    }

    public function update(UpdatePaymentRequest $request, Payment $payment, PaymentService $paymentService): RedirectResponse
    {
        $this->authorize('update', $payment);

        try {
            $paymentService->updatePosted($payment, $request->validated());
        } catch (\App\Exceptions\DocumentException $e) {
            return back()->withInput()->with('error', $e->getMessage());
        }

        if ($request->boolean('return_to_invoice') && $payment->invoice_id) {
            return redirect()
                ->route('invoices.show', $payment->invoice_id)
                ->with('success', 'Payment updated successfully.');
        }

        return redirect()
            ->route('payments.show', $payment)
            ->with('success', 'Payment updated successfully.');
    }

    public function reverse(ReversePaymentRequest $request, Payment $payment, PaymentService $paymentService): RedirectResponse
    {
        $this->authorize('reverse', $payment);

        $paymentService->reverse($payment, $request->validated('reason'));

        return back()->with('success', 'Payment reversed.');
    }

    public function refund(ReversePaymentRequest $request, Payment $payment, PaymentService $paymentService): RedirectResponse
    {
        $this->authorize('refund', $payment);

        $paymentService->refund($payment, $request->validated('reason'));

        return back()->with('success', 'Payment refunded.');
    }

    public function pdf(Payment $payment, PdfService $pdfService)
    {
        $this->authorize('view', $payment);

        return $pdfService->receipt($payment);
    }

    public function print(Payment $payment, SettingsService $settings): View
    {
        $this->authorize('view', $payment);

        $payment->load(['invoice.items', 'customer', 'receiver']);

        return view('pdf.receipt-print', [
            'payment' => $payment,
            'settings' => $settings->all(),
        ]);
    }
}
