<?php

namespace App\Services\Courier;

use App\CentralLogics\Helpers;
use App\Exceptions\CourierException;
use App\Models\Order;
use App\Models\OrderCourierShipment;
use Illuminate\Support\Facades\DB;

class CourierBookingService
{
    public function __construct(
        private readonly CourierSettingsService $settingsService,
        private readonly CourierPayloadBuilder $payloadBuilder,
        private readonly CourierProviderManager $providerManager,
        private readonly CourierLogService $logService,
        private readonly CourierCostResolver $courierCostResolver,
        private readonly CourierLedgerService $courierLedgerService
    ) {
    }

    public function sendToCourier(Order $order, ?int $adminId = null): OrderCourierShipment
    {
        return DB::transaction(function () use ($order, $adminId) {
            $order = Order::with(['details.item', 'customer', 'courierShipment'])->lockForUpdate()->findOrFail($order->id);

            if ($order->order_type === 'parcel') {
                throw new CourierException('Parcel module order-এর জন্য এই ecommerce courier automation apply হবে না।');
            }

            $integration = $this->settingsService->getIntegrationSettings();
            $providerKey = $this->settingsService->getActiveProvider();
            $providerConfig = $this->settingsService->getActiveProviderConfig();
            $senderProfile = $this->settingsService->getSenderProfile();

            if (! $this->settingsService->isIntegrationEnabled()) {
                throw new CourierException('Courier integration currently disabled আছে। আগে settings থেকে enable করতে হবে।');
            }

            if (! in_array($order->order_status, $integration['allowed_booking_statuses'] ?? ['confirmed', 'accepted', 'processing'], true)) {
                throw new CourierException('এই order status থেকে courier booking allowed না। আগে manual confirm করুন।');
            }

            $shipment = OrderCourierShipment::firstOrNew(['order_id' => $order->id]);
            if ($shipment->exists && $shipment->hasSuccessfulBooking()) {
                throw new CourierException('এই order already courier-এ পাঠানো হয়েছে। Duplicate parcel create করা যাবে না।');
            }

            $provider = $this->providerManager->driver($providerKey);
            $payload = $this->payloadBuilder->build($order, $senderProfile);
            $httpStatus = null;
            $responsePayload = [];

            try {
                $response = $provider->bookParcel($payload, $providerConfig);
                $httpStatus = $response['http_status'] ?? null;
                $responsePayload = $response['raw'] ?? [];

                if (! ($response['success'] ?? false)) {
                    $costData = $this->courierCostResolver->extract($responsePayload);

                    $shipment->fill([
                        'provider' => $providerKey,
                        'provider_mode' => $integration['mode'] ?? 'sandbox',
                        'booking_status' => 'failed',
                        'courier_status' => $response['courier_status'] ?? null,
                        'collectable_amount' => $payload['cod_amount'],
                        'actual_delivery_cost' => $costData['actual_delivery_cost'],
                        'actual_return_cost' => $costData['actual_return_cost'],
                        'actual_cod_charge' => $costData['actual_cod_charge'],
                        'parcel_note' => $payload['note'] ?? null,
                        'request_payload' => $payload,
                        'response_payload' => $responsePayload ?: null,
                        'last_http_status' => $httpStatus,
                        'last_error_message' => $response['message'] ?? 'Courier booking failed.',
                        'sent_by_admin_id' => $adminId,
                    ])->save();

                    $this->logService->write(
                        action: 'booking',
                        order: $order,
                        shipment: $shipment,
                        requestPayload: $payload,
                        responsePayload: $responsePayload,
                        httpStatus: $httpStatus,
                        errorMessage: $response['message'] ?? 'Courier booking failed.',
                        adminId: $adminId
                    );

                    throw new CourierException($response['message'] ?? 'Courier booking failed.');
                }

                $costData = $this->courierCostResolver->extract($responsePayload);

                $shipment->fill([
                    'provider' => $providerKey,
                    'provider_mode' => $integration['mode'] ?? 'sandbox',
                    'booking_status' => 'booked',
                    'courier_status' => $response['courier_status'] ?? null,
                    'courier_parcel_id' => $response['parcel_id'] ?? null,
                    'courier_tracking_id' => $response['tracking_id'] ?? null,
                    'courier_consignment_id' => $response['consignment_id'] ?? null,
                    'collectable_amount' => $payload['cod_amount'],
                    'actual_delivery_cost' => $costData['actual_delivery_cost'],
                    'actual_return_cost' => $costData['actual_return_cost'],
                    'actual_cod_charge' => $costData['actual_cod_charge'],
                    'parcel_note' => $payload['note'] ?? null,
                    'request_payload' => $payload,
                    'response_payload' => $responsePayload ?: null,
                    'last_http_status' => $httpStatus,
                    'last_error_message' => null,
                    'status_mapping_result' => 'handover',
                    'sent_to_courier_at' => now(),
                    'courier_synced_at' => now(),
                    'sent_by_admin_id' => $adminId,
                    'last_synced_by_admin_id' => $adminId,
                ])->save();

                $this->logService->write(
                    action: 'booking',
                    order: $order,
                    shipment: $shipment,
                    requestPayload: $payload,
                    responsePayload: $responsePayload,
                    httpStatus: $httpStatus,
                    errorMessage: null,
                    adminId: $adminId
                );

                $this->courierLedgerService->recordHandover($order, $shipment, $adminId);

                $order->order_status = 'handover';
                $order->handover = $order->handover ?: now();
                $order->save();
                Helpers::send_order_notification($order);

                return $shipment->fresh(['logs', 'sentByAdmin', 'lastSyncedByAdmin']);
            } catch (CourierException $exception) {
                throw $exception;
            } catch (\Throwable $throwable) {
                $costData = $this->courierCostResolver->extract($responsePayload);

                $shipment->fill([
                    'provider' => $providerKey,
                    'provider_mode' => $integration['mode'] ?? 'sandbox',
                    'booking_status' => 'failed',
                    'collectable_amount' => $payload['cod_amount'] ?? 0,
                    'actual_delivery_cost' => $costData['actual_delivery_cost'],
                    'actual_return_cost' => $costData['actual_return_cost'],
                    'actual_cod_charge' => $costData['actual_cod_charge'],
                    'parcel_note' => $payload['note'] ?? null,
                    'request_payload' => $payload ?? null,
                    'response_payload' => $responsePayload ?: null,
                    'last_http_status' => $httpStatus,
                    'last_error_message' => $throwable->getMessage(),
                    'sent_by_admin_id' => $adminId,
                ])->save();

                $this->logService->write(
                    action: 'booking',
                    order: $order,
                    shipment: $shipment,
                    requestPayload: $payload ?? [],
                    responsePayload: $responsePayload,
                    httpStatus: $httpStatus,
                    errorMessage: $throwable->getMessage(),
                    adminId: $adminId
                );

                throw new CourierException('Courier booking failed: ' . $throwable->getMessage());
            }
        });
    }
}
