<?php

use Illuminate\Database\QueryException;
use Modules\Booking\Enums\BookingChannel;
use Modules\Booking\Enums\BookingStatus;
use Modules\Booking\Models\Booking;
use Modules\Booking\Models\BookingVehicleOption;
use Modules\Catalog\Models\DepartureTimeSlot;
use Modules\Routing\Models\EvRoute;
use Modules\Routing\Models\RoutePricing;
use Modules\Shared\Enums\VehicleOption;

test('a booking belongs to a route and a time slot', function () {
    $route = EvRoute::factory()->create();
    $timeSlot = DepartureTimeSlot::factory()->create();

    $booking = Booking::factory()->create([
        'ev_route_id' => $route->id,
        'departure_time_slot_id' => $timeSlot->id,
    ]);

    expect($booking->route)->toBeInstanceOf(EvRoute::class)
        ->and($booking->route->is($route))->toBeTrue()
        ->and($booking->timeSlot)->toBeInstanceOf(DepartureTimeSlot::class)
        ->and($booking->timeSlot->is($timeSlot))->toBeTrue();
});

test('booking_ref is unique', function () {
    Booking::factory()->create(['booking_ref' => 'EVB-DUPLICATE']);

    expect(fn () => Booking::factory()->create(['booking_ref' => 'EVB-DUPLICATE']))
        ->toThrow(QueryException::class);
});

test('status and created_by_channel cast to their enums', function () {
    $booking = Booking::factory()->create([
        'status' => BookingStatus::Confirmed,
        'created_by_channel' => BookingChannel::Android,
    ]);

    expect($booking->status)->toBe(BookingStatus::Confirmed)
        ->and($booking->created_by_channel)->toBe(BookingChannel::Android);
});

test('a booking defaults to pending_payment', function () {
    $booking = Booking::factory()->create();

    expect($booking->status)->toBe(BookingStatus::PendingPayment);
});

test('a booking can have multiple vehicle option lines, e.g. front seat and back seat together', function () {
    $booking = Booking::factory()->create();

    BookingVehicleOption::factory()->create([
        'booking_id' => $booking->id,
        'vehicle_option' => VehicleOption::FrontSeat,
        'passenger_count' => 1,
        'unit_price' => 12000,
        'line_total' => 12000,
    ]);
    BookingVehicleOption::factory()->create([
        'booking_id' => $booking->id,
        'vehicle_option' => VehicleOption::BackSeat,
        'passenger_count' => 2,
        'unit_price' => 9000,
        'line_total' => 18000,
    ]);

    expect($booking->vehicleOptions)->toHaveCount(2)
        ->and($booking->vehicleOptions->pluck('vehicle_option')->map(fn ($option) => $option->value)->sort()->values()->all())
        ->toEqual(['back_seat', 'front_seat']);
});

test('a vehicle option line cannot be duplicated on the same booking', function () {
    $booking = Booking::factory()->create();

    BookingVehicleOption::factory()->create([
        'booking_id' => $booking->id,
        'vehicle_option' => VehicleOption::BackSeat,
    ]);

    expect(fn () => BookingVehicleOption::factory()->create([
        'booking_id' => $booking->id,
        'vehicle_option' => VehicleOption::BackSeat,
    ]))->toThrow(QueryException::class);
});

test('price is snapshotted onto the booking and does not change when RoutePricing is edited later', function () {
    $route = EvRoute::factory()->create();

    $pricing = RoutePricing::factory()->create([
        'ev_route_id' => $route->id,
        'vehicle_option' => VehicleOption::BackSeat,
        'price' => 15000,
    ]);

    $booking = Booking::factory()->create([
        'ev_route_id' => $route->id,
        'price' => $pricing->price,
    ]);

    BookingVehicleOption::factory()->create([
        'booking_id' => $booking->id,
        'vehicle_option' => VehicleOption::BackSeat,
        'unit_price' => $pricing->price,
        'line_total' => $pricing->price,
    ]);

    $pricing->update(['price' => 25000]);

    expect($booking->refresh()->price)->toEqual('15000.00')
        ->and($booking->vehicleOptions()->first()->unit_price)->toEqual('15000.00')
        ->and($pricing->refresh()->price)->toEqual('25000.00');
});

test('a booking can have a user or be guest-checked-out via mini app openid', function () {
    $guestBooking = Booking::factory()->create([
        'user_id' => null,
        'openid' => 'mini-app-openid-123',
    ]);

    expect($guestBooking->user_id)->toBeNull()
        ->and($guestBooking->openid)->toBe('mini-app-openid-123');
});
