<?php

namespace Modules\Booking\Actions;

use Illuminate\Support\Facades\DB;
use Modules\Booking\Data\CreateBookingData;
use Modules\Booking\Data\VehicleSelectionData;
use Modules\Booking\Enums\BookingStatus;
use Modules\Booking\Events\BookingCreated;
use Modules\Booking\Models\Booking;
use Modules\Booking\Services\BookingRefGenerator;
use Modules\Booking\Services\BookingService;
use Modules\Routing\Models\EvRoute;
use Modules\Routing\Services\PricingService;
use Modules\Shared\Enums\VehicleOption;

class CreateBookingAction
{
    public function __construct(
        private BookingService $bookingService,
        private PricingService $pricingService,
        private BookingRefGenerator $bookingRefGenerator,
    ) {}

    public function handle(CreateBookingData $data): Booking
    {
        $this->bookingService->validateSelections($data->selections);

        return DB::transaction(function () use ($data) {
            $route = EvRoute::findOrFail($data->evRouteId);

            $lines = array_map(
                fn (VehicleSelectionData $selection) => $this->priceSelection($route, $selection),
                $data->selections,
            );

            $totalPrice = array_reduce(
                $lines,
                fn (string $carry, array $line) => bcadd($carry, $line['line_total'], 2),
                '0.00',
            );

            $booking = Booking::create([
                'booking_ref' => $this->bookingRefGenerator->generate(),
                'user_id' => $data->userId,
                'openid' => $data->openid,
                'ev_route_id' => $data->evRouteId,
                'departure_time_slot_id' => $data->departureTimeSlotId,
                'travel_date' => $data->travelDate,
                'passenger_name' => $data->passengerName,
                'passenger_phone' => $data->passengerPhone,
                'pickup_address' => $data->pickupAddress,
                'pickup_lat' => $data->pickupLat,
                'pickup_lng' => $data->pickupLng,
                'dropoff_address' => $data->dropoffAddress,
                'dropoff_lat' => $data->dropoffLat,
                'dropoff_lng' => $data->dropoffLng,
                'price' => $totalPrice,
                'status' => BookingStatus::PendingPayment,
                'is_round_trip' => $data->isRoundTrip,
                'return_travel_date' => $data->returnTravelDate,
                'created_by_channel' => $data->createdByChannel,
            ]);

            $booking->vehicleOptions()->createMany($lines);

            BookingCreated::dispatch($booking);

            return $booking;
        });
    }

    /**
     * @return array{vehicle_option: VehicleOption, passenger_count: int, unit_price: string, line_total: string}
     */
    private function priceSelection(EvRoute $route, VehicleSelectionData $selection): array
    {
        $quote = $this->pricingService->quote($route, $selection->vehicleOption);

        return [
            'vehicle_option' => $selection->vehicleOption,
            'passenger_count' => $selection->passengerCount,
            'unit_price' => $quote->price,
            'line_total' => bcmul($quote->price, (string) $selection->passengerCount, 2),
        ];
    }
}
