<?php

namespace App\Http\Controllers;

use App\Models\Invoice;
use App\Models\Payment;
use App\Services\PaystackService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

class PaymentController extends Controller
{
    public function __construct(private PaystackService $paystack) {}

    // ─────────────────────────────────────────
    // INITIATE
    // Called via fetch() from the public invoice page
    // Returns Paystack authorization_url + reference
    // ─────────────────────────────────────────
    public function initiate(Request $request)
    {
        $request->validate([
            'token' => ['required', 'string'],
        ]);

        $invoice = Invoice::where('public_token', $request->token)
            ->with(['client', 'user'])
            ->firstOrFail();

        // Don't allow payment on already-paid or cancelled invoices
        if ($invoice->isPaid() || $invoice->isCancelled()) {
            return response()->json([
                'success' => false,
                'message' => 'This invoice is no longer payable.',
            ], 422);
        }

        // Use client email, fallback to business owner email
        $email = $invoice->client->email ?: $invoice->user->email;

        $reference = 'INV-' . strtoupper(Str::random(12));

        $result = $this->paystack->initializeTransaction([
            'email'        => $email,
            'amount'       => (int) ($invoice->total * 100), // convert to kobo
            'reference'    => $reference,
            'callback_url' => route('payments.callback'),
            'metadata'     => [
                'invoice_id'     => $invoice->id,
                'invoice_number' => $invoice->invoice_number,
                'invoice_token'  => $invoice->public_token,
                'business_name'  => $invoice->user->business_name,
            ],
        ]);

        if (! $result['success']) {
            return response()->json([
                'success' => false,
                'message' => $result['message'],
            ], 500);
        }

        return response()->json([
            'success'           => true,
            'authorization_url' => $result['authorization_url'],
            'reference'         => $result['reference'],
        ]);
    }

    // ─────────────────────────────────────────
    // CALLBACK
    // Paystack redirects the client browser here
    // after payment is completed (success or fail)
    // ─────────────────────────────────────────
    public function callback(Request $request)
    {
        $reference = $request->query('reference') ?? $request->query('trxref');

        if (! $reference) {
            return redirect('/')->with('error', 'No payment reference found.');
        }

        // Verify with Paystack — never trust the redirect alone
        $result = $this->paystack->verifyTransaction($reference);

        if (! $result['success'] || ! $result['paid']) {
            Log::warning('Paystack callback: payment not successful', [
                'reference' => $reference,
                'result'    => $result,
            ]);

            // Try to find the invoice from metadata to redirect back
            $invoice = Payment::where('paystack_reference', $reference)->first()?->invoice;

            return view('payments.failed', [
                'reference' => $reference,
                'invoice'   => $invoice,
            ]);
        }

        // Find invoice from the gateway response metadata
        $metadata  = $result['gateway_response']['metadata'] ?? [];
        $invoiceId = $metadata['invoice_id'] ?? null;
        $token     = $metadata['invoice_token'] ?? null;

        $invoice = $invoiceId
            ? Invoice::with(['client', 'user'])->find($invoiceId)
            : Invoice::with(['client', 'user'])->where('public_token', $token)->first();

        if (! $invoice) {
            Log::error('Paystack callback: invoice not found', ['reference' => $reference]);
            return view('payments.success', ['reference' => $reference, 'invoice' => null]);
        }

        // Process the payment (idempotent — safe to call twice)
        $this->processSuccessfulPayment($invoice, $result);

        return view('payments.success', compact('invoice', 'reference'));
    }

    // ─────────────────────────────────────────
    // WEBHOOK
    // Paystack calls this in the background
    // This is the reliable payment confirmation
    // Add route: POST /api/payments/webhook
    // Add to bootstrap/app.php CSRF except list
    // ─────────────────────────────────────────
    public function webhook(Request $request)
    {
        $payload   = $request->getContent();
        $signature = $request->header('x-paystack-signature', '');

        // Always verify the webhook signature
        if (! $this->paystack->verifyWebhookSignature($payload, $signature)) {
            Log::warning('Paystack webhook: invalid signature');
            return response()->json(['message' => 'Invalid signature'], 401);
        }

        $event = $request->json('event');
        $data  = $request->json('data');

        Log::info('Paystack webhook received', ['event' => $event]);

        if ($event === 'charge.success') {
            $reference = $data['reference'] ?? null;

            if (! $reference) {
                return response()->json(['message' => 'No reference'], 400);
            }

            // Avoid processing twice
            if (Payment::where('paystack_reference', $reference)
                        ->where('paystack_status', 'success')
                        ->exists()) {
                return response()->json(['message' => 'Already processed'], 200);
            }

            $result = $this->paystack->verifyTransaction($reference);

            if ($result['success'] && $result['paid']) {
                $metadata  = $result['gateway_response']['metadata'] ?? [];
                $invoiceId = $metadata['invoice_id'] ?? null;
                $token     = $metadata['invoice_token'] ?? null;

                $invoice = $invoiceId
                    ? Invoice::with(['client', 'user'])->find($invoiceId)
                    : Invoice::with(['client', 'user'])->where('public_token', $token)->first();

                if ($invoice) {
                    $this->processSuccessfulPayment($invoice, $result);
                }
            }
        }

        // Always return 200 to Paystack so it stops retrying
        return response()->json(['message' => 'Webhook received'], 200);
    }

    // ─────────────────────────────────────────
    // SHARED: process a successful payment
    // Idempotent — checks for duplicate reference
    // ─────────────────────────────────────────
    private function processSuccessfulPayment(Invoice $invoice, array $result): void
    {
        // Idempotency check — don't process the same reference twice
        if (Payment::where('paystack_reference', $result['reference'])->exists()) {
            return;
        }

        DB::transaction(function () use ($invoice, $result) {
            // Record the payment
            Payment::create([
                'invoice_id'             => $invoice->id,
                'amount'                 => $result['amount'],
                'payment_method'         => 'paystack',
                'paystack_reference'     => $result['reference'],
                'paystack_transaction_id'=> $result['transaction_id'],
                'paystack_status'        => 'success',
                'channel'                => $result['channel'],
                'currency'               => $result['currency'],
                'gateway_response'       => $result['gateway_response'],
                'paid_at'                => now(),
            ]);

            // Update invoice status
            $invoice->update([
                'status'  => 'paid',
                'paid_at' => now(),
            ]);

            // Update client totals
            $invoice->client->recalculateTotals();

            // Send confirmation emails
            $this->sendPaymentEmails($invoice);
        });

        Log::info('Payment processed successfully', [
            'invoice'   => $invoice->invoice_number,
            'reference' => $result['reference'],
            'amount'    => $result['amount'],
        ]);
    }

    // ─────────────────────────────────────────
    // Send payment emails to client + business
    // ─────────────────────────────────────────
    private function sendPaymentEmails(Invoice $invoice): void
    {
        try {
            // Email to client
            if ($invoice->client->email) {
                Mail::to($invoice->client->email)
                    ->send(new \App\Mail\PaymentReceived($invoice, 'client'));
            }

            // Email to business owner
            if ($invoice->user->notify_payment_received) {
                Mail::to($invoice->user->email)
                    ->send(new \App\Mail\PaymentReceived($invoice, 'owner'));
            }
        } catch (\Exception $e) {
            Log::error('Payment email failed: ' . $e->getMessage(), [
                'invoice' => $invoice->invoice_number,
            ]);
        }
    }
}
