<?php

namespace EasySales\Integrari\Model;

use EasySales\Integrari\Api\OrderManagementInterface;
use EasySales\Integrari\Core\Auth\CheckWebsiteToken;
use EasySales\Integrari\Core\Transformers\Order;
use EasySales\Integrari\Helper\Data;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
use Magento\Customer\Model\CustomerFactory;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\Group;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Model\Quote;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Pdf\Invoice as InvoicePdf;
use Magento\Sales\Model\Order\Pdf\Shipment as ShipmentPdf;
use Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory as InvoiceCollectionFactory;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Directory\Model\RegionFactory;
use Magento\Framework\App\ObjectManager;

class OrderManagement extends CheckWebsiteToken implements OrderManagementInterface
{
    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;

    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteria;

    /**
     * @var Order
     */
    private $_orderService;
    /**
     * @var Data
     */
    protected $helperData;

    /**
     * @var CartManagementInterface
     */
    private $cartManagement;

    /**
     * @var CartRepositoryInterface
     */
    private $cartRepository;

    /**
     * @var ProductRepositoryInterface
     */
    private $productRepository;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var CustomerRepositoryInterface
     */
    private $customerRepository;

    /**
     * @var CustomerFactory
     */
    private $customerFactory;

    /**
     * @var InvoicePdf
     */
    private $invoicePdf;

    /**
     * @var ShipmentPdf
     */
    private $shipmentPdf;

    /**
     * @var InvoiceCollectionFactory
     */
    private $invoiceCollectionFactory;

    /**
     * @var RegionFactory
     */
    private $regionFactory;

    /**
     * @var ProductInterfaceFactory
     */
    private $productFactory;

    /**
     * Reserved SKU for the synthetic order-fee line.
     */
    const FEE_SKU = 'es-order-fee';

    /**
     * Maximum grand-total residual the connector will silently reconcile (cross-currency rounding noise).
     */
    const MAX_ROUNDING_RESIDUAL = 0.05;

    /**
     * OrderManagement constructor.
     * @param Data $helperData
     * @param Request $request
     * @param OrderRepositoryInterface $orderRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param Order $orderService
     * @param CartManagementInterface $cartManagement
     * @param CartRepositoryInterface $cartRepository
     * @param ProductRepositoryInterface $productRepository
     * @param StoreManagerInterface $storeManager
     * @param CustomerRepositoryInterface $customerRepository
     * @param CustomerFactory $customerFactory
     * @param InvoicePdf $invoicePdf
     * @param InvoiceCollectionFactory $invoiceCollectionFactory
     * @throws \Exception
     */
    public function __construct(
        Data                        $helperData,
        Request                     $request,
        OrderRepositoryInterface    $orderRepository,
        SearchCriteriaBuilder       $searchCriteriaBuilder,
        Order                       $orderService,
        CartManagementInterface     $cartManagement,
        CartRepositoryInterface     $cartRepository,
        ProductRepositoryInterface  $productRepository,
        StoreManagerInterface       $storeManager,
        CustomerRepositoryInterface $customerRepository,
        CustomerFactory             $customerFactory,
        InvoicePdf                  $invoicePdf,
        ShipmentPdf                 $shipmentPdf,
        InvoiceCollectionFactory    $invoiceCollectionFactory,
        RegionFactory               $regionFactory,
        ProductInterfaceFactory     $productFactory
    )
    {
        parent::__construct($request, $helperData);

        $this->helperData = $helperData;
        $this->orderRepository = $orderRepository;
        $this->searchCriteria = $searchCriteriaBuilder;
        $this->_orderService = $orderService;
        $this->cartManagement = $cartManagement;
        $this->cartRepository = $cartRepository;
        $this->productRepository = $productRepository;
        $this->storeManager = $storeManager;
        $this->customerRepository = $customerRepository;
        $this->customerFactory = $customerFactory;
        $this->invoicePdf = $invoicePdf;
        $this->shipmentPdf = $shipmentPdf;
        $this->invoiceCollectionFactory = $invoiceCollectionFactory;
        $this->regionFactory = $regionFactory;
        $this->productFactory = $productFactory;
    }

    /**
     * @return array|mixed
     */
    public function getOrders()
    {
        $page = $this->request->getQueryValue('page', 1);
        $limit = $this->request->getQueryValue('limit', self::PER_PAGE);
        $lastCall = $this->request->getQueryValue('last_call');
        $this->searchCriteria
            ->addFilter('store_id', $this->helperData->getGeneralConfig('store_id'))
            ->setPageSize($limit)
            ->setCurrentPage($page);
        if ($lastCall) {
            $this->searchCriteria
                ->addFilter('updated_at', $lastCall, 'gt');
        }

        $list = $this->orderRepository->getList($this->searchCriteria->create());
        $orders = [];

        foreach ($list->getItems() as $order) {
            $orders[] = $this->_orderService->transform($order)->toArray();
        }

        return [[
            'perPage' => $limit,
            'pages' => ceil($list->getTotalCount() / $limit),
            'curPage' => $page,
            'orders' => $orders,
        ]];
    }

    /**
     * @param string $orderId
     * @return array[]
     */
    public function updateOrder(string $orderId)
    {
        $searchCriteria = $this->searchCriteria
            ->addFilter('increment_id', $orderId, 'eq')->create();
        $orderList = $this->orderRepository->getList($searchCriteria)->getItems();

        $order = end($orderList);
        $data = $this->request->getBodyParams();
        switch ($data['status']) {
            case 'Completed':
                $orderStatus = \Magento\Sales\Model\Order::STATE_COMPLETE;
                break;
            case 'Canceled':
                $orderStatus = \Magento\Sales\Model\Order::STATE_CANCELED;
                break;
            default:
                $orderStatus = \Magento\Sales\Model\Order::STATE_PROCESSING;
                break;
        }
        $order->setState($orderStatus)
            ->setStatus($orderStatus);
        try {
            if ($orderStatus == \Magento\Sales\Model\Order::STATE_COMPLETE) {
                $orderShipment = $this->findOrCreateOrderShipment($order);

                if ($orderShipment) {
                    $trackFactory = ObjectManager::getInstance()->get('Magento\Sales\Model\Order\Shipment\TrackFactory');

                    $tracksCollection = $order->getTracksCollection();

                    $trackNumbers = [];
                    foreach ($tracksCollection->getItems() as $track) {
                        $trackNumbers[] = $track->getTrackNumber();
                    }

                    foreach ($data['shipments'] ?? [] as $shipment) {
                        $barcodeNumbers = [];

                        if (!empty($shipment['awb_barcode'] ?? [])) {
                            $barcodeNumbers = $shipment['awb_barcode'];
                        } else {
                            $barcodeNumbers = $shipment['awb_id'] ?? [];
                        }

                        foreach ($barcodeNumbers ?? [] as $barcode) {
                            if (!in_array($barcode, $trackNumbers)) {
                                $track = $trackFactory->create();
                                $track->setNumber($barcode);
                                $track->setCarrierCode($shipment['courier']['name'] ?? 'Unknown');
                                $track->setTitle($shipment['courier']['name'] ?? 'Unknown');

                                $orderShipment->addTrack($track);
                                $orderShipment->save();

                                //$orderShipment->delete();
                            }
                        }
                    }
                }
            }

            $this->orderRepository->save($order);
        } catch (\Exception $exception) {
            return [[
                "success" => false,
                "message" => $exception->getMessage(),
            ]];
        }

        return [[
            "success" => true,
            "order" => $order->getIncrementId(),
            'status' => $data['status'],
        ]];
    }

    public function findOrCreateOrderShipment($order)
    {
        if ($order->hasShipments()) {
            $shipmentCollection = $order->getShipmentsCollection();
            $shipment = $shipmentCollection->getFirstItem();
        } else {
            $convertOrder = ObjectManager::getInstance()->get('Magento\Sales\Model\Convert\Order');
            $shipment = $convertOrder->toShipment($order);
            foreach ($order->getAllItems() as $orderItem) {
                if (!$orderItem->getQtyToShip() || $orderItem->getIsVirtual()) {
                    //continue;
                }

                $qtyOrdered = $orderItem->getQtyOrdered();
                $shipmentItem = $convertOrder->itemToShipmentItem($orderItem)->setQty($qtyOrdered);
                $shipment->addItem($shipmentItem);

                $shipment->register();
            }

            if (method_exists($shipment->getExtensionAttributes(), 'setSourceCode')) {
                $stockSource = $this->helperData->getGeneralConfig('stock_source') ?? null;

                if ($stockSource) {
                    $shipment->getExtensionAttributes()->setSourceCode($stockSource);
                } else {
                    $shipment->getExtensionAttributes()->setSourceCode('default');
                }
            }

            $shipment->save();
        }


        return $shipment;
    }

    /**
     * @param string $orderId
     * @return array[]
     */
    public function getOrder(string $orderId)
    {
        try {
            $searchCriteria = $this->searchCriteria
                ->addFilter('increment_id', $orderId, 'eq')->create();
            $orderList = $this->orderRepository->getList($searchCriteria)->getItems();

            $order = end($orderList);
            if (!$order) {
                throw new \Exception("Order not found");
            }
            return [[
                "order" => $this->_orderService->transform($order)->toArray(),
            ]];
        } catch (\Exception $exception) {
            return [[
                "success" => false,
                "message" => $exception->getMessage(),
            ]];
        }
    }

    /**
     * @return array[]
     */
    public function createOrder()
    {
        try {
            $data = $this->request->getBodyParams();

            $storeId = $data['store_id'] ?? $this->helperData->getGeneralConfig('store_id');
            $store = $this->storeManager->getStore($storeId);
            $websiteId = $store->getWebsiteId();

            $billingData = $data['billing_address'] ?? [];
            $shippingData = $data['shipping_address'] ?? $billingData;

            $customer = $data['customer'] ?? [];
            $email = $customer['email'] ?? ($billingData['email'] ?? null);
            if (!$email) {
                throw new \Exception("Customer email is required");
            }

            $cartId = $this->cartManagement->createEmptyCart();
            /** @var Quote $quote */
            $quote = $this->cartRepository->get($cartId);
            $quote->setStore($store);
            $quote->setCurrency();

            // Quote-level super mode mirrors admin order creation: it bypasses the legacy
            // CatalogInventory quantity validator (which only honors the quote-level flag) and
            // keeps option/price handling consistent with the per-product flag set below.
            $quote->setIsSuperMode(true);

            $this->attachCustomer($quote, $customer, $email, $websiteId);

            // Net prices + per-line tax class; Magento computes the VAT.
            foreach ($data['order_products'] ?? [] as $line) {
                $sku = $line['sku'] ?? null;
                if (!$sku) {
                    return [[
                        "success" => false,
                        "message" => "Product SKU is missing",
                    ]];
                }

                try {
                    $product = $this->productRepository->get($sku, false, $store->getId());
                } catch (\Exception $exception) {
                    return [[
                        "success" => false,
                        "message" => "SKU " . $sku . " not found",
                    ]];
                }

                $taxClassId = $this->resolveTaxClassId((float)($line['tax_rate'] ?? 0));
                $product->setIsSuperMode(true);
                $product->setData('is_super_mode', true);
                $product->setTaxClassId($taxClassId);

                $quoteItem = $quote->addProduct($product, (float)($line['quantity'] ?? 1));
                if (is_string($quoteItem) || $quoteItem instanceof \Magento\Framework\Phrase) {
                    return [[
                        "success" => false,
                        "message" => (string)$quoteItem,
                    ]];
                }

                $net = (float)($line['price_without_tax'] ?? 0);
                $quoteItem->setCustomPrice($net);
                $quoteItem->setOriginalCustomPrice($net);
                $quoteItem->getProduct()->setIsSuperMode(true)->setTaxClassId($taxClassId);
            }

            // Fees: one taxable line summing all fee nets, labelled with the easySales fee name(s).
            $feeNet = 0.0;
            $feeRate = 0.0;
            $feeNames = [];
            foreach ($data['fees'] ?? [] as $fee) {
                $feeNet += (float)($fee['price_without_tax'] ?? 0);
                $feeRate = max($feeRate, (float)($fee['tax_rate'] ?? 0));
                if (!empty($fee['name'])) {
                    $feeNames[] = $fee['name'];
                }
            }
            if ($feeNet != 0.0) {
                $feeTaxClassId = $this->resolveTaxClassId($feeRate);
                $feeProduct = $this->ensureFeeProduct($store);
                $feeProduct->setIsSuperMode(true)->setTaxClassId($feeTaxClassId);

                $feeItem = $quote->addProduct($feeProduct, 1);
                if (is_string($feeItem) || $feeItem instanceof \Magento\Framework\Phrase) {
                    return [[
                        "success" => false,
                        "message" => (string)$feeItem,
                    ]];
                }
                $feeItem->setCustomPrice($feeNet);
                $feeItem->setOriginalCustomPrice($feeNet);
                $feeItem->setName(!empty($feeNames) ? implode(', ', $feeNames) : 'Fee');
                $feeItem->getProduct()->setIsSuperMode(true)->setTaxClassId($feeTaxClassId);
            }

            $quote->getBillingAddress()
                ->addData($this->buildAddress($billingData, $email))
                ->setShouldIgnoreValidation(true);
            $shippingAddress = $quote->getShippingAddress();
            $shippingAddress
                ->addData($this->buildAddress($shippingData, $email))
                ->setShouldIgnoreValidation(true);

            // Shipping: net flatrate line; the sent VAT rate is applied as shipping tax post-place.
            $shipmentGross = (float)($data['shipment']['price_with_tax'] ?? 0);
            $shippingRate = (float)($data['shipment']['tax_rate'] ?? 0);
            $shippingNet = $shippingRate > 0 ? round($shipmentGross / (1 + $shippingRate), 2) : $shipmentGross;
            $shippingTax = round($shipmentGross - $shippingNet, 2);
            $shippingAddress->setCollectShippingRates(true)
                ->collectShippingRates()
                ->setShippingMethod('flatrate_flatrate');

            $paymentCode = $data['payment_code'] ?? 'checkmo';
            $quote->setPaymentMethod($paymentCode);
            $quote->setInventoryProcessed(false);
            $quote->getPayment()->importData(['method' => $paymentCode]);

            $vatId = $customer['vat_id'] ?? ($billingData['vat_id'] ?? null);
            if ($vatId) {
                $quote->setCustomerTaxvat($vatId);
            }

            $externalId = $data['external_id'] ?? null;
            $quote->setData('easysales_should_send', false);

            $quote->collectTotals();
            $this->forceShippingAmount($quote, $shippingNet);
            $this->cartRepository->save($quote);

            $orderId = $this->cartManagement->placeOrder($quote->getId());
            $order = $this->orderRepository->get($orderId);

            // Discounts: summed into the native item-level discount_amount.
            $discountAmount = 0.0;
            foreach ($data['discounts'] ?? [] as $discount) {
                $discountAmount += abs((float)($discount['price_without_tax'] ?? 0));
            }
            if ($discountAmount > 0) {
                $this->applyOrderDiscount($order, $discountAmount);
            }

            // Quote-item name is overwritten during conversion, so label the fee on the order item.
            if (!empty($feeNames)) {
                $feeLineName = implode(', ', $feeNames);
                foreach ($order->getAllItems() as $orderItem) {
                    if ($orderItem->getSku() === self::FEE_SKU) {
                        $orderItem->setName($feeLineName);
                    }
                }
            }

            // Honor the exact per-line VAT sent by easySales (Magento otherwise taxes each line by
            // its catalog tax class, which mis-taxes zero-rated or mixed-rate products).
            $sentRates = [];
            foreach ($data['order_products'] ?? [] as $line) {
                if (!empty($line['sku'])) {
                    $sentRates[$line['sku']] = (float)($line['tax_rate'] ?? 0);
                }
            }
            $taxAmount = 0.0;
            foreach ($order->getAllItems() as $item) {
                $rate = $item->getSku() === self::FEE_SKU ? $feeRate : ($sentRates[$item->getSku()] ?? 0);
                $rowNet = (float)$item->getRowTotal();
                $rowTax = round($rowNet * $rate, 2);
                $unitInclTax = (float)$item->getPrice() + round((float)$item->getPrice() * $rate, 2);
                $item->setTaxPercent($rate * 100)
                    ->setTaxAmount($rowTax)->setBaseTaxAmount($rowTax)
                    ->setPriceInclTax($unitInclTax)->setBasePriceInclTax($unitInclTax)
                    ->setRowTotalInclTax($rowNet + $rowTax)->setBaseRowTotalInclTax($rowNet + $rowTax);
                $taxAmount += $rowTax;
            }
            $taxAmount = round($taxAmount + $shippingTax, 2);

            // Shipping is a net line carrying the sent VAT rate as its tax; rebuild the grand from the components.
            $order->setShippingAmount($shippingNet)
                ->setBaseShippingAmount($shippingNet)
                ->setShippingInclTax($shipmentGross)
                ->setBaseShippingInclTax($shipmentGross)
                ->setShippingTaxAmount($shippingTax)
                ->setBaseShippingTaxAmount($shippingTax);
            $order->setTaxAmount($taxAmount)->setBaseTaxAmount($taxAmount);
            $grandTotal = round(
                (float)$order->getSubtotal() + $taxAmount + $shippingNet - (float)$order->getDiscountAmount(),
                2
            );

            // easySales is the source of truth for the grand total. Cross-currency conversion rounds each
            // line independently, so the rebuilt grand can drift a cent from it. Absorb the residual into the
            // last taxable line's tax (the invoice/credit-memo copy the stored item tax) so all reconcile exactly.
            $authoritativeTotal = round((float)($data['order_total'] ?? 0), 2);
            $residual = $authoritativeTotal > 0 ? round($authoritativeTotal - $grandTotal, 2) : 0.0;
            if ($residual !== 0.0 && abs($residual) <= self::MAX_ROUNDING_RESIDUAL) {
                $adjustItem = null;
                foreach ($order->getAllItems() as $item) {
                    if ($item->getTaxPercent() > 0) {
                        $adjustItem = $item;
                    }
                }
                if ($adjustItem) {
                    $itemTax = round((float)$adjustItem->getTaxAmount() + $residual, 2);
                    $adjustItem->setTaxAmount($itemTax)
                        ->setBaseTaxAmount($itemTax)
                        ->setRowTotalInclTax((float)$adjustItem->getRowTotal() + $itemTax)
                        ->setBaseRowTotalInclTax((float)$adjustItem->getRowTotal() + $itemTax);
                    $taxAmount = round($taxAmount + $residual, 2);
                    $order->setTaxAmount($taxAmount)->setBaseTaxAmount($taxAmount);
                    $grandTotal = $authoritativeTotal;
                }
            }

            $order->setGrandTotal($grandTotal)->setBaseGrandTotal($grandTotal);

            $order->setData('easysales_should_send', false);
            $order->setExtOrderId($externalId);
            if ($vatId) {
                $order->setCustomerTaxvat($vatId);
            }
            $order->setCanSendNewEmailFlag(false);

            if ($externalId) {
                $order->addCommentToStatusHistory(
                    __('Imported from easySales. External ID: %1', $externalId),
                    false,
                    false
                );
            }

            $this->orderRepository->save($order);

            return [[
                "success" => true,
                "increment_id" => $order->getIncrementId(),
                "order_id" => $order->getEntityId(),
            ]];
        } catch (\Exception $exception) {
            return [[
                "success" => false,
                "message" => $exception->getMessage(),
            ]];
        }
    }

    /**
     * Map a VAT rate to a Magento product tax class. 0% => None (0); any positive rate => the
     * store's taxable class (its tax rule for the customer region must equal the marketplace rate).
     */
    private function resolveTaxClassId(float $rate): int
    {
        if ($rate <= 0) {
            return 0;
        }

        return (int)($this->helperData->getGeneralConfig('taxable_class_id') ?: 2);
    }

    /**
     * Get (or create once) the hidden virtual product used to represent the order fee as a line.
     */
    private function ensureFeeProduct($store)
    {
        try {
            return $this->productRepository->get(self::FEE_SKU, false, $store->getId());
        } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
            $product = $this->productFactory->create();
            $product->setSku(self::FEE_SKU)
                ->setName('Fee')
                ->setAttributeSetId(4)
                ->setTypeId('virtual')
                ->setPrice(0)
                ->setStatus(1)
                ->setVisibility(1)
                ->setTaxClassId((int)($this->helperData->getGeneralConfig('taxable_class_id') ?: 2))
                ->setStockData(['use_config_manage_stock' => 0, 'is_in_stock' => 1]);

            return $this->productRepository->save($product);
        }
    }

    /**
     * Apply the summed discount via the native item-level discount_amount, distributed across the
     * product lines proportionally to their row total (the last line absorbs the rounding remainder).
     * The fee line is excluded. The merchant-created invoice/credit-memo collectors honor this.
     */
    private function applyOrderDiscount($order, float $discount)
    {
        $items = [];
        $base = 0.0;
        foreach ($order->getAllItems() as $item) {
            if ($item->getSku() === self::FEE_SKU) {
                continue;
            }
            $items[] = $item;
            $base += (float)$item->getRowTotal();
        }
        if (empty($items) || $base <= 0) {
            return;
        }

        $count = count($items);
        $allocated = 0.0;
        $index = 0;
        foreach ($items as $item) {
            $index++;
            if ($index === $count) {
                $share = round($discount - $allocated, 2);
            } else {
                $share = round($discount * ((float)$item->getRowTotal() / $base), 2);
                $allocated += $share;
            }
            $item->setDiscountAmount($share)->setBaseDiscountAmount($share);
        }

        $order->setDiscountAmount($discount)->setBaseDiscountAmount($discount);
        $order->setGrandTotal($order->getGrandTotal() - $discount)
            ->setBaseGrandTotal($order->getBaseGrandTotal() - $discount);
    }

    /**
     * @param string $orderId
     * @return array[]
     */
    public function getOrderDocuments(string $orderId)
    {
        try {
            $searchCriteria = $this->searchCriteria
                ->addFilter('increment_id', $orderId, 'eq')->create();
            $orderList = $this->orderRepository->getList($searchCriteria)->getItems();

            $order = end($orderList);
            if (!$order) {
                throw new \Exception("Order not found");
            }

            $invoices = [];
            $invoiceCollection = $this->invoiceCollectionFactory->create()
                ->addAttributeToFilter('order_id', $order->getEntityId());

            foreach ($invoiceCollection as $invoice) {
                $pdfString = $this->invoicePdf->getPdf([$invoice])->render();

                $invoices[] = [
                    'increment_id' => $invoice->getIncrementId(),
                    'number' => $invoice->getIncrementId(),
                    'created_at' => $invoice->getCreatedAt(),
                    'grand_total' => (float)$invoice->getGrandTotal(),
                    'pdf_base64' => base64_encode($pdfString),
                ];
            }

            $shipments = [];
            foreach ($order->getShipmentsCollection() as $shipment) {
                $tracks = [];
                foreach ($shipment->getTracksCollection() as $track) {
                    $tracks[] = [
                        'carrier_code' => $track->getCarrierCode(),
                        'title' => $track->getTitle(),
                        'track_number' => $track->getTrackNumber(),
                    ];
                }

                $shippingLabel = $shipment->getShippingLabel();

                $shipments[] = [
                    'increment_id' => $shipment->getIncrementId(),
                    'pdf_base64' => $shippingLabel ? base64_encode($shippingLabel) : null,
                    'tracks' => $tracks,
                ];
            }

            return [[
                'invoices' => $invoices,
                'shipments' => $shipments,
            ]];
        } catch (\Exception $exception) {
            return [[
                "success" => false,
                "message" => $exception->getMessage(),
            ]];
        }
    }

    /**
     * @param Quote $quote
     * @param array $customer
     * @param string $email
     * @param int $websiteId
     * @return void
     */
    private function attachCustomer($quote, $customer, $email, $websiteId)
    {
        $customerModel = $this->customerFactory->create();
        $customerModel->setWebsiteId($websiteId)->loadByEmail($email);

        if ($customerModel->getEntityId()) {
            $customerData = $this->customerRepository->getById($customerModel->getEntityId());
            $quote->assignCustomer($customerData);
            $quote->setCustomerIsGuest(0);

            return;
        }

        $quote->setCustomerId(null);
        $quote->setCustomerEmail($email);
        $quote->setCustomerIsGuest(true);
        $quote->setCustomerGroupId(Group::NOT_LOGGED_IN_ID);
        $quote->setCheckoutMethod('guest');

        $name = (string)($customer['name'] ?? '');
        list($firstname, $lastname) = $this->splitName($name);
        $quote->setCustomerFirstname($firstname);
        $quote->setCustomerLastname($lastname);
    }

    /**
     * @param array $address
     * @param string $email
     * @return array
     */
    private function buildAddress($address, $email)
    {
        list($firstname, $lastname) = $this->splitName((string)($address['name'] ?? ''));

        $countryId = $address['country'] ?? '';
        $regionName = $address['county'] ?? '';

        return [
            'firstname' => $firstname,
            'lastname' => $lastname,
            'company' => $address['company_name'] ?? null,
            'street' => $address['street'] ?? '',
            'city' => $address['city'] ?? '',
            'country_id' => $countryId,
            'region' => $regionName,
            'region_id' => $this->resolveRegionId($countryId, $regionName),
            'postcode' => $address['postal_code'] ?? '',
            'telephone' => $address['phone'] ?? '',
            'vat_id' => $address['vat_id'] ?? null,
            'email' => $email,
        ];
    }

    /**
     * Resolve a Magento region id from a free-text county name for the given country.
     * Marketplace addresses carry only the county name, so we match it against
     * Magento's directory regions (by name then code); 0 when there is no match.
     *
     * @param string $countryId
     * @param string $regionName
     * @return int
     */
    private function resolveRegionId($countryId, $regionName)
    {
        if (!$countryId || !$regionName) {
            return 0;
        }

        $region = $this->regionFactory->create()->loadByName($regionName, $countryId);
        if ($region->getId()) {
            return (int)$region->getId();
        }

        $region = $this->regionFactory->create()->loadByCode($regionName, $countryId);

        return (int)($region->getId() ?? 0);
    }

    /**
     * @param string $name
     * @return array
     */
    private function splitName($name)
    {
        $name = trim($name);
        if ($name === '') {
            return ['', ''];
        }

        $parts = explode(' ', $name, 2);
        $firstname = $parts[0];
        $lastname = isset($parts[1]) && trim($parts[1]) !== '' ? trim($parts[1]) : $firstname;

        return [$firstname, $lastname];
    }

    /**
     * @param Quote $quote
     * @param float $amount
     * @return void
     */
    private function forceShippingAmount($quote, $amount)
    {
        $shippingAddress = $quote->getShippingAddress();
        $shippingAddress->setShippingMethod('flatrate_flatrate');
        $shippingAddress->setShippingDescription('Marketplace Shipping');
        $shippingAddress->setShippingAmount($amount);
        $shippingAddress->setBaseShippingAmount($amount);
        $shippingAddress->setCollectShippingRates(false);
    }
}
