<?php

namespace App\Http\Controllers;

use App\Mail\PaymentReceipt;
use App\Models\ActivityLog;
use App\Models\Booking;
use App\Models\Order;
use App\Models\Payment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

/**
 * Guest-facing online checkout for bookings and food/drink orders,
 * via Paystack or Flutterwave. Flow: show → init (creates a pending
 * Payment + redirects to the gateway) → callback (verifies & settles).
 */
class PayController extends Controller
{
    public function show(string $type, string $code)
    {
        [$entity, $balance, $description] = $this->resolve($type, $code);

        $gateways = array_filter([
            config('services.paystack.secret_key') ? 'paystack' : null,
            config('services.flutterwave.secret_key') ? 'flutterwave' : null,
        ]);

        return view('site.pay', compact('entity', 'balance', 'description', 'type', 'code', 'gateways'));
    }

    public function init(Request $request, string $type, string $code)
    {
        $request->validate([
            'gateway' => 'required|in:paystack,flutterwave',
            'amount' => 'required|numeric|min:100',
            'email' => 'required|email',
        ]);

        [$entity, $balance] = $this->resolve($type, $code);

        $amount = min((float) $request->amount, max($balance, 0.01));
        $reference = 'EPY-' . strtoupper(Str::random(12));

        Payment::create([
            'code' => 'PAY-' . strtoupper(Str::random(6)),
            'payable_type' => $type,
            'payable_id' => $entity->id,
            'customer_id' => $entity->customer_id,
            'amount' => $amount,
            'method' => 'online',
            'status' => 'pending',
            'reference' => $reference,
        ]);

        if ($request->gateway === 'paystack') {
            $response = Http::withToken(config('services.paystack.secret_key'))
                ->post('https://api.paystack.co/transaction/initialize', [
                    'email' => $request->email,
                    'amount' => (int) round($amount * 100), // kobo
                    'reference' => $reference,
                    'callback_url' => route('pay.callback', 'paystack'),
                    'metadata' => ['type' => $type, 'code' => $code],
                ]);

            if ($response->successful() && ($url = $response->json('data.authorization_url'))) {
                return redirect()->away($url);
            }
        } else {
            $response = Http::withToken(config('services.flutterwave.secret_key'))
                ->post('https://api.flutterwave.com/v3/payments', [
                    'tx_ref' => $reference,
                    'amount' => $amount,
                    'currency' => 'NGN',
                    'redirect_url' => route('pay.callback', 'flutterwave'),
                    'customer' => ['email' => $request->email, 'name' => $entity->customer->name ?? 'Guest'],
                    'customizations' => ['title' => 'Epelcon Hospitality', 'description' => strtoupper($type) . ' ' . $code],
                ]);

            if ($response->successful() && ($url = $response->json('data.link'))) {
                return redirect()->away($url);
            }
        }

        report(new \RuntimeException('Gateway init failed: ' . $response->body()));

        return back()->with('error', 'We could not reach the payment gateway. Please try again or pay at the front desk.');
    }

    public function callback(Request $request, string $gateway)
    {
        $reference = $gateway === 'paystack'
            ? $request->query('reference')
            : $request->query('tx_ref');

        $payment = Payment::where('reference', $reference)->first();

        if (! $payment) {
            return redirect()->route('home')->with('error', 'We could not match that payment. Contact reception if you were debited.');
        }

        if ($payment->status === 'completed') {
            return $this->receiptRedirect($payment, 'This payment was already confirmed.');
        }

        $verified = $gateway === 'paystack'
            ? $this->verifyPaystack($reference, (float) $payment->amount)
            : $this->verifyFlutterwave($request, (float) $payment->amount);

        if (! $verified) {
            $payment->update(['status' => 'failed']);

            return $this->receiptRedirect($payment, null, 'Payment was not successful. You have not been charged — please try again.');
        }

        $payment->update(['status' => 'completed', 'paid_at' => now()]);
        $this->settle($payment);

        ActivityLog::record('online payment received', 'finance', "{$payment->code} via {$gateway}: " . number_format($payment->amount, 2));

        return $this->receiptRedirect($payment, 'Payment successful! A receipt has been sent to your email. 🎉');
    }

    // ------------------------------------------------------------------

    private function resolve(string $type, string $code): array
    {
        abort_unless(in_array($type, ['booking', 'order']), 404);

        if ($type === 'booking') {
            $entity = Booking::with(['customer', 'roomType'])->where('code', $code)->firstOrFail();

            return [$entity, $entity->balance(), 'Booking ' . $entity->code . ' — ' . ($entity->roomType->name ?? 'Room')];
        }

        $entity = Order::with('customer')->where('code', $code)->firstOrFail();

        return [$entity, (float) $entity->total - (float) $entity->paid_amount, 'Order ' . $entity->code];
    }

    private function verifyPaystack(string $reference, float $expected): bool
    {
        $response = Http::withToken(config('services.paystack.secret_key'))
            ->get("https://api.paystack.co/transaction/verify/{$reference}");

        return $response->successful()
            && $response->json('data.status') === 'success'
            && (float) $response->json('data.amount') >= round($expected * 100);
    }

    private function verifyFlutterwave(Request $request, float $expected): bool
    {
        $transactionId = $request->query('transaction_id');

        if (! $transactionId || $request->query('status') === 'cancelled') {
            return false;
        }

        $response = Http::withToken(config('services.flutterwave.secret_key'))
            ->get("https://api.flutterwave.com/v3/transactions/{$transactionId}/verify");

        return $response->successful()
            && $response->json('data.status') === 'successful'
            && (float) $response->json('data.amount') >= $expected
            && $response->json('data.currency') === 'NGN';
    }

    private function settle(Payment $payment): void
    {
        if ($payment->payable_type === 'booking') {
            $booking = Booking::find($payment->payable_id);
            $booking?->increment('paid_amount', $payment->amount);
            $email = $booking?->customer?->email;
            $description = 'Booking ' . ($booking->code ?? '');
        } else {
            $order = Order::find($payment->payable_id);

            if ($order) {
                $paid = (float) $order->paid_amount + (float) $payment->amount;
                $order->update([
                    'paid_amount' => $paid,
                    'payment_method' => 'online',
                    'payment_status' => $paid >= (float) $order->total ? 'paid' : 'partial',
                ]);
            }

            $email = $order?->customer?->email;
            $description = 'Order ' . ($order->code ?? '');
        }

        if ($email) {
            rescue(fn () => Mail::to($email)->send(new PaymentReceipt($payment->fresh(), $description)), report: false);
        }
    }

    private function receiptRedirect(Payment $payment, ?string $success = null, ?string $error = null)
    {
        $route = $payment->payable_type === 'booking'
            ? route('pay.show', ['type' => 'booking', 'code' => Booking::find($payment->payable_id)?->code ?? ''])
            : route('pay.show', ['type' => 'order', 'code' => Order::find($payment->payable_id)?->code ?? '']);

        return redirect($route)->with($success ? 'success' : 'error', $success ?? $error);
    }
}
