<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Booking;
use App\Models\Payment;
use App\Models\Vehicle;
use App\Services\Pricing\PricingEngine;
use App\Support\Pricing\PricingQuote;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\RedirectResponse;

class PublicBookingPortalController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Jimny portal (3-door / 5-door, no specific car)
    |--------------------------------------------------------------------------
    */

    public function showJimnyForm(Request $request): View
    {
        $brandKey = 'jimny';

        return view('public-booking.jimny', [
            'brandKey' => $brandKey,
        ]);
    }

    public function storeJimny(Request $request): RedirectResponse
    {
        $brandKey = 'jimny';

        $data = $request->validate([
            'pickup_at'          => ['required', 'date'],
            'return_at'          => ['required', 'date', 'after:pickup_at'],
            'pickup_location_id' => ['nullable'],
            'return_location_id' => ['nullable'],

            'vehicle_type'       => ['required', 'in:jimny_3door,jimny_5door'],

            'customer_name'      => ['required', 'string', 'max:255'],
            'customer_email'     => ['required', 'email', 'max:255'],
            'customer_phone'     => ['required', 'string', 'max:50'],
            'notes'              => ['nullable', 'string', 'max:1000'],
        ]);

        $vehicleLabel = $data['vehicle_type'] === 'jimny_5door'
            ? 'Jimny 5-door'
            : 'Jimny 3-door';

        try {
            DB::beginTransaction();

            /** @var PricingEngine $engine */
            $engine = app(PricingEngine::class);

            /** @var PricingQuote $quote */
            $quote = $engine->quoteDailyRate(
                brand: $brandKey,
                vehicle: null,
                vehicleType: $data['vehicle_type'],
                pickupAt: $data['pickup_at'],
                returnAt: $data['return_at'],
                context: [
                    'source'         => 'jimny_portal',
                    'portal_brand'   => $brandKey,
                    'requested_type' => $data['vehicle_type'],
                ],
            );

            $totalAmountCents   = $quote->totalCents;
            $depositAmountCents = intdiv($totalAmountCents, 2);

            $meta = [
                'customer' => [
                    'name'  => $data['customer_name'],
                    'email' => $data['customer_email'],
                    'phone' => $data['customer_phone'],
                    'notes' => $data['notes'] ?? null,
                ],
                'portal' => [
                    'brand'   => $brandKey,
                    'channel' => $brandKey . '_portal',
                ],
                'requested_vehicle_type' => $data['vehicle_type'],
                'pricing'                => $quote->meta,
            ];

            $bookingTable = (new Booking)->getTable();

            $bookingData = [
                'customer_id'  => null,
                'vehicle_id'   => null, // assigned later
                'vehicle_name' => $vehicleLabel,

                'brand'         => $brandKey,
                'source_system' => $brandKey . '_portal',
                'source_id'     => null,

                'pickup_at' => $data['pickup_at'],
                'return_at' => $data['return_at'],
                'start_at'  => $data['pickup_at'],
                'end_at'    => $data['return_at'],

                'total_amount'   => $totalAmountCents,
                'deposit_amount' => $depositAmountCents,
                'hold_amount'    => 0,
                'paid_amount'    => 0,
                'currency'       => 'NZD',

                'status'          => 'pending_payment',
                'meta'            => $meta,
                'billing_address' => null,
            ];

            // Only include columns that actually exist on the bookings table
            $bookingDataFiltered = collect($bookingData)
                ->filter(fn ($value, $column) => Schema::hasColumn($bookingTable, $column))
                ->all();

            /** @var Booking $booking */
            $booking = Booking::create($bookingDataFiltered);

            // Create a booking_deposit Payment + link for the hosted pay page
            $payUrl = $this->createBookingDepositPaymentAndLink(
                booking: $booking,
                depositAmountCents: $depositAmountCents,
                brandKey: $brandKey,
                sourceKey: 'jimny_portal',
            );

            DB::commit();
        } catch (\Throwable $e) {
            DB::rollBack();
            report($e);

            return redirect()
                ->route('book.jimny.show')
                ->with('error', 'Something went wrong while processing your booking. Please try again or contact us.');
        }

        // If payment link failed, just go to the thank-you page
        if (! $payUrl) {
            return redirect()
                ->route('public.booking.thank-you', ['ref' => $booking->reference])
                ->with('success', 'Thanks! Your booking is saved. We’ll be in touch to arrange payment.');
        }

        // Otherwise send them straight to the hosted payment page
        return redirect()->away($payUrl);
    }

    /*
    |--------------------------------------------------------------------------
    | Dream Drives portal (pick a specific vehicle, per-vehicle pricing)
    |--------------------------------------------------------------------------
    */

    public function showDreamDrivesForm(Request $request): View
    {
        $brandKey     = 'dreamdrives';
        $vehicleTable = (new Vehicle)->getTable();

        $vehicles = Vehicle::query()
            ->when(
                Schema::hasColumn($vehicleTable, 'brand'),
                fn ($q) => $q->where('brand', $brandKey)
            )
            ->when(
                Schema::hasColumn($vehicleTable, 'is_active'),
                fn ($q) => $q->where('is_active', true)
            )
            ->orderBy('id')
            ->get();

        return view('public-booking.dream-drives', [
            'brandKey' => $brandKey,
            'vehicles' => $vehicles,
        ]);
    }

    public function storeDreamDrives(Request $request): RedirectResponse
    {
        $brandKey = 'dreamdrives';

        $data = $request->validate([
            'vehicle_id'         => ['required', 'exists:vehicles,id'],
            'pickup_at'          => ['required', 'date'],
            'return_at'          => ['required', 'date', 'after:pickup_at'],
            'pickup_location_id' => ['nullable'],
            'return_location_id' => ['nullable'],

            'customer_name'  => ['required', 'string', 'max:255'],
            'customer_email' => ['required', 'email', 'max:255'],
            'customer_phone' => ['required', 'string', 'max:50'],

            'notes'          => ['nullable', 'string', 'max:1000'],
        ]);

        $vehicleTable = (new Vehicle)->getTable();

        $vehicleQuery = Vehicle::query()->whereKey($data['vehicle_id']);

        if (Schema::hasColumn($vehicleTable, 'brand')) {
            $vehicleQuery->where('brand', $brandKey);
        }

        /** @var Vehicle $vehicle */
        $vehicle = $vehicleQuery->firstOrFail();

        try {
            DB::beginTransaction();

            /** @var PricingEngine $engine */
            $engine = app(PricingEngine::class);

            /** @var PricingQuote $quote */
            $quote = $engine->quoteDailyRate(
                brand: $brandKey,
                vehicle: $vehicle,
                vehicleType: $vehicle->type ?? null,
                pickupAt: $data['pickup_at'],
                returnAt: $data['return_at'],
                context: [
                    'source'       => 'dreamdrives_portal',
                    'portal_brand' => $brandKey,
                ],
            );

            $totalAmountCents   = $quote->totalCents;
            $depositAmountCents = intdiv($totalAmountCents, 2);

            $meta = [
                'customer' => [
                    'name'  => $data['customer_name'],
                    'email' => $data['customer_email'],
                    'phone' => $data['customer_phone'],
                    'notes' => $data['notes'] ?? null,
                ],
                'portal' => [
                    'brand'   => $brandKey,
                    'channel' => $brandKey . '_portal',
                ],
                'vehicle' => [
                    'id'   => $vehicle->id,
                    'type' => $vehicle->type ?? null,
                ],
                'pricing' => $quote->meta,
            ];

            $bookingTable = (new Booking)->getTable();

            $bookingData = [
                'customer_id'  => null,
                'vehicle_id'   => $vehicle->id,
                'vehicle_name' => $vehicle->display_name,

                'brand'         => $brandKey,
                'source_system' => $brandKey . '_portal',
                'source_id'     => null,

                'pickup_at' => $data['pickup_at'],
                'return_at' => $data['return_at'],
                'start_at'  => $data['pickup_at'],
                'end_at'    => $data['return_at'],

                'total_amount'   => $totalAmountCents,
                'deposit_amount' => $depositAmountCents,
                'hold_amount'    => 0,
                'paid_amount'    => 0,
                'currency'       => 'NZD',

                'status'          => 'pending_payment',
                'meta'            => $meta,
                'billing_address' => null,
            ];

            $bookingDataFiltered = collect($bookingData)
                ->filter(fn ($value, $column) => Schema::hasColumn($bookingTable, $column))
                ->all();

            /** @var Booking $booking */
            $booking = Booking::create($bookingDataFiltered);

            $payUrl = $this->createBookingDepositPaymentAndLink(
                booking: $booking,
                depositAmountCents: $depositAmountCents,
                brandKey: $brandKey,
                sourceKey: 'dreamdrives_portal',
            );

            DB::commit();
        } catch (\Throwable $e) {
            DB::rollBack();
            report($e);

            return redirect()
                ->route('book.dreamdrives.show')
                ->with('error', 'Something went wrong while processing your booking. Please try again or contact us.');
        }

        if (! $payUrl) {
            return redirect()
                ->route('public.booking.thank-you', ['ref' => $booking->reference])
                ->with('success', 'Thanks! Your booking is saved. We’ll be in touch to arrange payment.');
        }

        return redirect()->away($payUrl);
    }

    /*
    |--------------------------------------------------------------------------
    | Thank-you page
    |--------------------------------------------------------------------------
    */

    public function thankYou(Request $request): View
    {
        $ref = $request->query('ref');

        $booking = null;

        if ($ref) {
            $booking = Booking::where('reference', $ref)->first();
        }

        return view('public-booking.thank-you', [
            'reference' => $ref,
            'booking'   => $booking,
        ]);
    }

    /*
    |--------------------------------------------------------------------------
    | Internal helper – create Payment + PaymentLink for booking deposit
    |--------------------------------------------------------------------------
    */

    protected function createBookingDepositPaymentAndLink(
        Booking $booking,
        int $depositAmountCents,
        string $brandKey,
        string $sourceKey,
    ): ?string {
        $paymentsTable = (new Payment)->getTable();

        // 1) Work out a tenant_id we can safely use for the PaymentLink
        $tenantId = null;

        // Prefer booking.tenant_id if it exists & is set
        if (Schema::hasColumn($booking->getTable(), 'tenant_id') && ! empty($booking->tenant_id)) {
            $tenantId = (int) $booking->tenant_id;
        }

        // Otherwise, reuse any existing payment tenant_id if the column exists
        if ($tenantId === null && Schema::hasColumn($paymentsTable, 'tenant_id')) {
            $existingTenantId = Payment::query()
                ->whereNotNull('tenant_id')
                ->value('tenant_id');

            if ($existingTenantId) {
                $tenantId = (int) $existingTenantId;
            }
        }

        // Final fallback: even if payments table has no tenant_id, we
        // still want *some* value in memory for Payment::createLink().
        if ($tenantId === null) {
            $tenantId = 1;
        }

        // 2) Build payment data (no hard dependency on tenant_id column here)
        $paymentData = [
            'booking_id'   => $booking->id,
            'currency'     => 'NZD',
            'status'       => 'pending',
            'type'         => 'booking_deposit',
            'amount_cents' => $depositAmountCents,
            'meta'         => [
                'brand'       => $brandKey,
                'source'      => $sourceKey,
                'booking_ref' => $booking->reference ?? null,
                'customer'    => $booking->meta['customer'] ?? null,
            ],
        ];

        // Filter to real columns on the payments table
        $paymentDataFiltered = collect($paymentData)
            ->filter(fn ($value, $column) => Schema::hasColumn($paymentsTable, $column))
            ->all();

        /** @var Payment $payment */
        $payment = Payment::create($paymentDataFiltered);

        // 3) Set tenant_id *in memory* so createLink() can see it,
        // regardless of whether the payments table actually has that column.
        $payment->tenant_id = $tenantId;

        // 4) Try to create a PaymentLink. If it fails, log and return null
        try {
            $link = $payment->createLink();
        } catch (\Throwable $e) {
            report($e);
            return null;
        }

        // Build the public pay URL using the existing route
        return route('payments.public.show', ['token' => $link->public_token]);
    }
}
