<?php

declare(strict_types=1);

/*
 * This file is part of the VitiCore 2.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Product\Model\ProductPrice;
use CpCreation\VitiCore\Product\Model\ProductType;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use CpCreation\VitiCore\Product\Repository\ProductTaxRepository;
use CpCreation\VitiCore\Product\Repository\ProductTypeRepository;
use CpCreation\VitiCore\Shop\Model\DeliveryMethod;
use CpCreation\VitiCore\Shop\Model\ShippingAdditional;
use CpCreation\VitiCore\Shop\Model\ShippingConfig;
use CpCreation\VitiCore\Shop\Model\ShippingCountry;
use CpCreation\VitiCore\Shop\Model\ShippingPrice;
use CpCreation\VitiCore\Shop\Model\ShopConfigMultiple;
use CpCreation\VitiCore\Shop\Repository\ShippingAdditionalManualRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingAdditionalRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingAutoRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingAutoSupRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingConfigRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingCountryRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingCountyRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingPriceRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingZoneRepository;
use CpCreation\VitiCore\Shop\Repository\ShopConfigMultipleRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Intl\Countries;
use Symfony\Contracts\Translation\TranslatorInterface;

class CalculShipping
{
    private ?string $locale;

    public function __construct(
        private ProductPriceGroupRepository $productPriceGroupRepository,
        private ShippingConfigRepository $shippingConfigRepository,
        private ProductTypeRepository $productTypeRepository,
        private CartRepository $cartRepository,
        private ProductTaxRepository $productTaxRepository,
        private ShippingCountryRepository $shippingCountryRepository,
        private ShippingCountyRepository $shippingCountyRepository,
        private ShippingPriceRepository $shippingPriceRepository,
        private ShippingAutoRepository $shippingAutoRepository,
        private ShippingAutoSupRepository $shippingAutoSupRepository,
        private ShippingAdditionalManualRepository $shippingAdditionalManualRepository,
        private ShippingAdditionalRepository $shippingAdditionalRepository,
        private ShippingZoneRepository $shippingZoneRepository,
        private ShopConfigMultipleRepository $shopConfigMultipleRepository,
        private TranslatorInterface $translator,
        private RequestStack $requestStack,
    ) {
        $locale = $this->getSession()->get('_locale');
        $this->locale = \is_string($locale) ? $locale : null;
    }

    private function getSession(): ?SessionInterface
    {
        return $this->requestStack->getSession();
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     * @throws \Exception
     *
     * @return array{amount: float|int, shippingName: ?string}
     */
    public function shippingTotalPrice(Cart $cart): array
    {
        $items = $cart->getItems();
        $totalAmount = 0.0;

        /** @var DeliveryMethod|null $deliveryMethod */
        $deliveryMethod = $cart->getDeliveryMethod();
        $shippingDeliveryMethod = $this->usesMethodShippingPrices($deliveryMethod) ? $deliveryMethod : null;

        if ($cart->getDeliveryAddress()->isDifferentAddress()) {
            $codePostal = $cart->getDeliveryAddress()->getPostalCode();
            $city = $cart->getDeliveryAddress()->getCity();
            $countryCode = $cart->getDeliveryAddress()->getCountry();
        } else {
            $codePostal = $cart->getInvoiceAddress()->getPostalCode();
            $city = $cart->getInvoiceAddress()->getCity();
            $countryCode = $cart->getInvoiceAddress()->getCountry();
        }

        $priceGroupId = $this->getSession()->get('groupPrice');
        $priceGroup = $this->productPriceGroupRepository->find($priceGroupId);

        if (null === $priceGroup) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        /** @var array<int, ProductType> $types */
        $types = [];

        /** @var CartItem $item */
        foreach ($items as $item) {
            /** @var ProductPrice $price */
            foreach ($item->getVariant()->getPrices() as $price) {
                if ($price->getGroup()->getId()->toString() === $priceGroupId) {
                    if ($price->getPriceDiscount()) {
                        $totalAmount += $price->getPriceDiscount() * $item->getQuantity();
                    } else {
                        $totalAmount += $price->getPrice() * $item->getQuantity();
                    }
                }
            }

            if (!\in_array($item->getVariant()->getType(), $types, true)) {
                $types[] = $item->getVariant()->getType();
            }

            if (
                $item->getQuantity() > 0
                && $item->getVariant()->getType()->getMaximumQuantityCart() > 0
                && $item->getQuantity() > $item->getVariant()->getType()->getMaximumQuantityCart()
            ) {
                $messages = $this->translator->trans(
                    'shop.error.quantity_type_override_max',
                    [
                        '%quantity%' => $item->getVariant()->getType()->getMaximumQuantityCart(),
                        '%product%' => $item->getVariant()->getProduct()->getFullName(),
                        '%type%' => $item->getVariant()->getType()->getName(),
                    ],
                    null,
                    $this->locale
                ).'<br/>';

                throw new \Exception($messages);
            }

            $saleSinceStockAt = $this->cartRepository->stockSaleSinceStockAt($item->getVariant())[0]['quantitySale'] ?? 0;

            if ($saleSinceStockAt && (($item->getQuantity() + $saleSinceStockAt) > $item->getVariant()->getStock())) {
                $messages = $this->translator->trans(
                    'shop.error.quantity_type_sold_out',
                    [
                        '%stock%' => $item->getVariant()->getStock() - $saleSinceStockAt,
                        '%quantity%' => $item->getQuantity(),
                        '%product%' => $item->getVariant()->getProduct()->getFullName(),
                        '%type%' => $item->getVariant()->getType()->getName(),
                    ],
                    null,
                    $this->locale
                ).'<br/>';

                throw new \Exception($messages);
            }
        }

        /** @var ShippingConfig|null $shippingConfig */
        $shippingConfig = $this->shippingConfigRepository->findOne();

        if (null === $shippingConfig) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        if ($shippingConfig->getAmountFreeShipping() && $totalAmount >= $shippingConfig->getAmountFreeShipping()) {
            return ['amount' => 0, 'shippingName' => null];
        }

        $amountMarginBank = $shippingConfig->getAmountMarginBank() ?: 0;

        $typeDefault = $this->productTypeRepository->findDefaultType();
        $taxDefault = $this->productTaxRepository->findOneBy(['byDefault' => true]);

        if (null === $typeDefault || null === $taxDefault) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        $quantities = [];
        $quantitiesManu = [];
        $quantitiesMinCart = [];
        $quantitiesFreeShipping = [];
        $typesFreeShipping = [];
        $freeShippingWithBottle = [];
        $minFreeShippingWithBottle = [];
        $zonesFreeShippingWithBottle = [];
        $disabledShippingAuto = [];
        $disabledShippingManu = [];

        $fdpSupp = (int) ceil(($totalAmount * $shippingConfig->getMarginBank()) + $amountMarginBank);
        $fdp = null;
        $fdpTypeMarge = 0.0;
        $fdpTypeMargeWithoutMarge = 0.0;
        $fdpPrivateMargePercent = 0.0;
        $fdpPrivateMarge = 0.0;
        $fdpTypeMargeTotal = 0.0;
        $fdpTypeMargeWithoutMargeTotal = 0.0;
        $fdpManu = 0.0;
        $quantityAllAbsolute = 0;
        $quantityEquivalenceOff = 0;
        $quantityEquivalenceDefault = 0;
        $shippingName = '';
        $typeName = [];
        $equivalenceAuto = 0;

        $quantitiesMinCart[$typeDefault->getSlug()] = 0;
        $quantities[$typeDefault->getSlug()] = 0;
        $quantitiesManu[$typeDefault->getSlug()] = 0;
        $freeShippingWithBottle[$typeDefault->getSlug()] = false;
        $minFreeShippingWithBottle[$typeDefault->getSlug()] = null;
        $quantitiesFreeShipping[$typeDefault->getSlug()] = 0;
        $zonesFreeShippingWithBottle[$typeDefault->getSlug()] = [];
        $disabledShippingAuto[$typeDefault->getSlug()] = false;
        $disabledShippingManu[$typeDefault->getSlug()] = false;

        foreach ($types as $type) {
            $quantitiesMinCart[$type->getSlug()] = 0;
            $zonesFreeShippingWithBottle[$type->getSlug()] = $type->getZonesFreeShippingId();

            if (!$type->isEquivalencyOnShipping()) {
                $quantities[$type->getSlug()] = 0;
                $quantitiesManu[$type->getSlug()] = 0;
            }

            if ($type->isEquivalencyOnFreeShipping()) {
                $typesFreeShipping[] = $type->getSlug();
            }

            if ($type->isFreeShippingWithBottle()) {
                $freeShippingWithBottle[$type->getSlug()] = true;
                $minFreeShippingWithBottle[$type->getSlug()] = $type->getMinFreeShippingWithBottle();
            } else {
                $freeShippingWithBottle[$type->getSlug()] = false;
                $minFreeShippingWithBottle[$type->getSlug()] = null;
            }

            $disabledShippingManu[$type->getSlug()] = $type->isDisabledShippingManu();
            $disabledShippingAuto[$type->getSlug()] = $type->isDisabledShippingAuto();
        }

        /** @var CartItem $item */
        foreach ($items as $item) {
            if ($item->getVariant()->getType()->isEquivalencyOnFreeShipping()) {
                $quantitiesFreeShipping[$typeDefault->getSlug()] =
                    $quantities[$typeDefault->getSlug()]
                    + ($item->getQuantity() * $item->getVariant()->getType()->getEquivalencyFreeShipping());
            }

            if ($item->getVariant()->getType()->isEquivalencyOnShipping()) {
                $quantities[$typeDefault->getSlug()] += $item->getQuantity() * $item->getVariant()->getType()->getEquivalency();
                $quantitiesManu[$typeDefault->getSlug()] += $item->getQuantity() * $item->getVariant()->getType()->getEquivalencyManu();
                $quantityEquivalenceDefault += $item->getQuantity() * $item->getVariant()->getType()->getEquivalencyManu();
            } else {
                $quantities[$item->getVariant()->getType()->getSlug()] += $item->getQuantity();
                $quantitiesManu[$item->getVariant()->getType()->getSlug()] += $item->getQuantity();
            }

            if ($item->getVariant()->getType()->isEquivalencyDefaultQuantity()) {
                $quantitiesMinCart[$typeDefault->getSlug()] += $item->getQuantity() * $item->getVariant()->getType()->getEquivalency();
            } else {
                $quantitiesMinCart[$item->getVariant()->getType()->getSlug()] += $item->getQuantity();
            }

            if ($item->getVariant()->getType()->isEquivalencyDefault()) {
                $quantityEquivalenceDefault += $item->getQuantity() * $item->getVariant()->getType()->getEquivalencyManu();
            }

            $quantityEquivalenceOff += $item->getQuantity();

            if (
                !$disabledShippingAuto[$item->getVariant()->getType()->getSlug()]
                && !$item->getVariant()->getType()->isAccessory()
            ) {
                $equivalenceAuto += $item->getQuantity() * $item->getVariant()->getType()->getEquivalency();
            }

            if (!$item->getVariant()->getType()->isAccessory()) {
                $quantityAllAbsolute += $item->getQuantity() * $item->getVariant()->getType()->getEquivalency();
            }
        }

        if ($shippingConfig->getQtyEquivalencyBottle() && $quantityAllAbsolute > $shippingConfig->getQtyEquivalencyBottle()) {
            $quantities = [];
            $quantitiesManu = [];
            $quantities[$typeDefault->getSlug()] = $quantityAllAbsolute;
            $quantitiesManu[$typeDefault->getSlug()] = $quantityAllAbsolute;
        }

        /** @var ShippingCountry|null $country */
        $country = $this->shippingCountryRepository->findOneByGroup($countryCode, $priceGroupId);
        $county = null;
        $zone = null;

        if ($country) {
            if ($country->isCounty()) {
                if (null === $county) {
                    throw new \Exception($this->translator->trans('shop.error.empty_shipping', [], null, $this->locale));
                }
                $department = mb_substr((string) $codePostal, 0, 2);
                $county = $this->shippingCountyRepository->findDepartmentByCountry($department, $country->getCode());
            }

            $zone = $this->shippingZoneRepository->findByCountryAndByCounty($country, $county);
        }

        $messages = null;
        $shopConfigMultiple = null;

        if ((!$country && !$deliveryMethod) || (!$country && $shippingDeliveryMethod)) {
            throw new \Exception($this->translator->trans('shop.error.error_country', [], null, $this->locale));
        }

        if (($country && !$deliveryMethod) || ($country && $shippingDeliveryMethod)) {
            if ($country->isCounty() && !$codePostal) {
                throw new \Exception($this->translator->trans('shop.error.postal_code', [], null, $this->locale));
            }
        }

        if ($items->count() < 1) {
            throw new \Exception($this->translator->trans('shop.empty_cart', [], null, $this->locale));
        }

        $typesbySlug = array_map(
            static fn (ProductType $type): string => $type->getSlug(),
            $types
        );

        if (!\in_array($typeDefault->getSlug(), $typesbySlug, true)) {
            $types[] = $typeDefault;
        }

        if (!$deliveryMethod || !$deliveryMethod->isMultipleVariant()) {
            /** @var ProductType $type */
            foreach ($types as $type) {
                $typeName[$type->getSlug()] = $type->getName();

                if (null === $messages) {
                    $messages = '';
                }

                $typeMultipleQuantityCart = $type->getMultipleQuantityCart();

                if ($zone) {
                    /** @var ShopConfigMultiple|null $shopConfigMultiple */
                    $shopConfigMultiple = $this->shopConfigMultipleRepository->findByQuantityAndByTypeAndByZone(
                        $quantitiesMinCart[$type->getSlug()],
                        $type->getSlug(),
                        $zone
                    );

                    if ($shopConfigMultiple) {
                        $typeMultipleQuantityCart = $shopConfigMultiple->getMultiple();
                    }
                }

                if ($type->getOrderWithaMinimumEquivalency() && ($type->getOrderWithaMinimumEquivalency() > $quantityAllAbsolute)) {
                    $messages .= $this->translator->trans(
                        'shop.error.quantity_type_min_equivalency',
                        [
                            '%min%' => $type->getOrderWithaMinimumEquivalency(),
                            '%type%' => $type->getName(),
                        ],
                        null,
                        $this->locale
                    ).'<br/>';
                }

                if (($quantitiesMinCart[$type->getSlug()] % $typeMultipleQuantityCart) > 0) {
                    if ($shopConfigMultiple) {
                        if ($shopConfigMultiple->isMinimum()) {
                            $messages .= $this->translator->trans(
                                'shop.error.quantity_type_override_min',
                                [
                                    '%quantity%' => $typeMultipleQuantityCart,
                                    '%type%' => $type->getName(),
                                ],
                                null,
                                $this->locale
                            ).'<br/>';
                        } else {
                            $messages .= $this->translator->trans(
                                'shop.error.quantity_type_override',
                                [
                                    '%quantity%' => $typeMultipleQuantityCart,
                                    '%min%' => $shopConfigMultiple->getQtyOf(),
                                    '%max%' => $shopConfigMultiple->getQtyAt(),
                                    '%type%' => $type->getName(),
                                ],
                                null,
                                $this->locale
                            ).'<br/>';
                        }
                    } else {
                        $messages .= $this->translator->trans(
                            'shop.error.quantity_type',
                            [
                                '%quantity%' => $typeMultipleQuantityCart,
                                '%type%' => $type->getName(),
                            ],
                            null,
                            $this->locale
                        ).'<br/>';
                    }
                }
            }

            if ($messages) {
                throw new \Exception($messages);
            }
        }

        $messages = '';
        $county = null;

        if ($country) {
            if ($country->isCounty()) {
                $department = mb_substr((string) $codePostal, 0, 2);
                $county = $this->shippingCountyRepository->findDepartmentByCountry($department, $country->getCode());

                if (!$county && !$deliveryMethod) {
                    throw new \Exception($this->translator->trans('shop.error.empty_shipping', [], null, $this->locale));
                }
            }

            if ($country->getMinQty() && $country->getMultiple() && !$deliveryMethod) {
                if ((($quantitiesMinCart[$typeDefault->getSlug()] % $country->getMultiple()) > 0) || ($quantitiesMinCart[$typeDefault->getSlug()] < $country->getMinQty())) {
                    $message = $this->translator->trans(
                        'shop.error.error_country_qty',
                        [
                            '%multiple%' => $country->getMultiple(),
                            '%quantity%' => $country->getMinQty(),
                            '%type%' => $typeDefault->getName(),
                            '%country%' => Countries::getName($country->getCode()),
                        ],
                        null,
                        $this->locale
                    ).'<br/>';

                    throw new \Exception($message);
                }
            }
        }

        if ($deliveryMethod) {
            $errorProducts = [];
            $errorQuantityType = [];

            /** @var CartItem $item */
            foreach ($items as $item) {
                if ($deliveryMethod->isMultipleVariant() && $item->getVariant()->getType()->getMultipleQuantityDepot() > 0) {
                    if (($item->getQuantity() % $item->getVariant()->getType()->getMultipleQuantityDepot()) > 0) {
                        $errorQuantityType[] = [
                            'type' => $item->getVariant()->getType()->getName(),
                            'quantity' => $item->getVariant()->getType()->getMultipleQuantityDepot(),
                        ];
                    }
                }

                if ($deliveryMethod->getProductVariants()->count() > 0 && !$deliveryMethod->getProductVariants()->contains($item->getVariant())) {
                    $errorProducts[] = $item->getVariant()->getName();
                }
            }

            if (\count($errorProducts) > 0) {
                $messages .= $this->translator->trans(
                    'shop.error.error_product_delivery_method',
                    ['%products%' => implode(', ', $errorProducts)],
                    null,
                    $this->locale
                ).'<br/>';
            } elseif (\count($errorQuantityType) > 0) {
                foreach ($errorQuantityType as $errorQuantity) {
                    $messages .= $this->translator->trans(
                        'shop.error.error_product_delivery_method_quantity_variant',
                        [
                            '%type%' => $errorQuantity['type'],
                            '%quantity%' => $errorQuantity['quantity'],
                        ],
                        null,
                        $this->locale
                    ).'<br/>';
                }
            }

            if ($deliveryMethod->getQtyAt() > $quantityAllAbsolute) {
                $messages .= $this->translator->trans('shop.delivery_method.error_qty', [], null, $this->locale).'<br/>';
            }

            if (!$shippingDeliveryMethod) {
                if ($deliveryMethod->isViewCounties() && !$deliveryMethod->getCounties()->contains($county)) {
                    $messages .= $this->translator->trans('shop.delivery_method.error_county', [], null, $this->locale).'<br/>';
                }
            }

            if ('' !== $messages) {
                throw new \Exception($messages);
            }

            if (
                !$deliveryMethod->isEnabledShippingPrice()
                && !$shippingDeliveryMethod
                && null !== $deliveryMethod->getPackagePrice()
                && $deliveryMethod->getPackagePrice() >= 0
            ) {
                return ['amount' => $deliveryMethod->getPackagePrice(), 'shippingName' => null];
            }

            if (!$deliveryMethod->isEnabledShippingPrice() && !$shippingDeliveryMethod && $deliveryMethod->getBottlePrice()) {
                if ($deliveryMethod->isDisabledEquivalence()) {
                    return [
                        'amount' => $deliveryMethod->getBottlePrice() * $quantityEquivalenceOff,
                        'shippingName' => null,
                    ];
                }

                return [
                    'amount' => $deliveryMethod->getBottlePrice() * $quantityAllAbsolute,
                    'shippingName' => null,
                ];
            }
        }

        $equivalence = 0;
        $shippingConfig = $this->shippingConfigRepository->findOne();

        if (null === $shippingConfig) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        $i = 0;
        $zoneTest = null;
        $quantitiesManu = array_reverse($quantitiesManu);
        $errorTypes = [];

        if ($country && $country->isCounty() && $equivalenceAuto > 0 && $shippingConfig->isForceEquivalencyForAuto()) {
            $equivalenceAutoShipping = $this->shippingPriceRepository->findByQuantityAndByTypeAndByZoneAndByGroup(
                $equivalenceAuto,
                $typeDefault->getSlug(),
                $county?->getZone(),
                $priceGroupId,
                $shippingDeliveryMethod
            );

            if (!$equivalenceAutoShipping) {
                foreach ($quantitiesManu as $type => $quantity) {
                    if (!$disabledShippingAuto[$type]) {
                        unset($quantitiesManu[$type]);
                    }
                }

                $equivalence = $equivalenceAuto;
            }
        }

        foreach ($quantitiesManu as $type => $quantity) {
            if ($quantity > 0 && false === $disabledShippingManu[$type]) {
                $shippingPrice = null;

                /* @var ShippingPrice|null $shippingPrice */
                if ($country && $country->isCounty()) {
                    $shippingPrice = $this->shippingPriceRepository->findByQuantityAndByTypeAndByZoneAndByGroup(
                        $quantity,
                        $type,
                        $county?->getZone(),
                        $priceGroupId,
                        $shippingDeliveryMethod
                    );

                    if (0 === $i && $county?->getZone()) {
                        $zoneTest = $county->getZone()->getId()->toString();
                    }
                } elseif ($country) {
                    foreach ($country->getZones() as $zone) {
                        $shippingPrice = $this->shippingPriceRepository->findByQuantityAndByTypeAndByZoneAndByGroup(
                            $quantity,
                            $type,
                            $zone,
                            $priceGroupId,
                            $shippingDeliveryMethod
                        );

                        if ($shippingPrice) {
                            if (0 === $i) {
                                $zoneTest = $zone->getId()->toString();
                            }

                            break;
                        }
                    }
                }

                if ($shippingPrice) {
                    $shippingName = $shippingPrice->getNameCarrier();

                    if (
                        $freeShippingWithBottle[$type]
                        && (!$minFreeShippingWithBottle[$type] || $quantityEquivalenceDefault >= $minFreeShippingWithBottle[$type])
                        && \in_array($zoneTest, $zonesFreeShippingWithBottle[$type], true)
                    ) {
                        $fdpType = 0.0;
                    } else {
                        if ($shippingPrice->getBottlePrice()) {
                            $fdpType = $shippingPrice->getBottlePrice() * $quantity;
                        } else {
                            $fdpType = $shippingPrice->getPackagePrice();
                        }

                        if ($shippingPrice->getPrivateMargePercent() > 0) {
                            $fdpPrivateMargePercent = (float) ceil($fdpType * $shippingPrice->getPrivateMargePercent());
                        }

                        if ($shippingPrice->getPrivateMarge() > 0) {
                            $fdpPrivateMarge = $shippingPrice->getPrivateMarge();
                        }
                    }

                    $fdpType += $fdpPrivateMargePercent + $fdpPrivateMarge;

                    if ($shippingPrice->isConfigGlobal()) {
                        $fdpTypeMarge = $fdpType;
                    } else {
                        $fdpTypeMargeWithoutMarge = $fdpType;
                    }

                    $fdpTypeMargeTotal += $fdpTypeMarge;
                    $fdpTypeMargeWithoutMargeTotal += $fdpTypeMargeWithoutMarge;

                    unset($quantitiesManu[$type], $quantities[$type]);

                    $fdp = 0;
                } else {
                    if ($shippingDeliveryMethod) {
                        throw new \Exception($this->translator->trans('shop.error.missing_shipping_delivery_method', [], null, $this->locale));
                    }

                    if ($quantity > 0 && true === $disabledShippingAuto[$type]) {
                        $errorTypes[] = $typeName[$type].' x'.$quantity;
                    }

                    $productType = $this->productTypeRepository->findTypeBySlug($type);
                    $equivalence += $quantity * $productType->getEquivalency();

                    unset($quantitiesManu[$type]);

                    if ($typeDefault->getSlug() !== $type) {
                        unset($quantities[$type]);
                    }
                }
            } else {
                if ($quantity > 0 && false === $disabledShippingAuto[$type]) {
                    $productType = $this->productTypeRepository->findTypeBySlug($type);

                    if ($type !== $typeDefault->getSlug()) {
                        $quantitiesManu[$typeDefault->getSlug()] += $quantity * $productType->getEquivalency();
                    }

                    $equivalence += $quantity * $productType->getEquivalency();
                } elseif ($quantity > 0 && true === $disabledShippingAuto[$type]) {
                    $errorTypes[] = $typeName[$type].' x'.$quantity;
                }
            }

            ++$i;
        }

        if (\count($errorTypes) > 0) {
            throw new \Exception($this->translator->trans('shop.error.error_product_delivery_method_standard', ['%types%' => implode(', ', $errorTypes)], null, $this->locale).'<br/>');
        }

        if ($fdpTypeMargeTotal > 0) {
            if ($shippingConfig->getAmountFixedManu() > 0) {
                $fdpTypeMargeTotal += $shippingConfig->getAmountFixedManu();
            }

            if ($shippingConfig->getAmountMinVDManu() > 0 && $quantityAllAbsolute >= $shippingConfig->getQtyMinVDManu()) {
                $taxeVD = round(($fdpTypeMargeTotal + $totalAmount) * $shippingConfig->getMarginManu(), 2);

                if ($taxeVD < $shippingConfig->getAmountMinVDManu()) {
                    $taxeVD = $shippingConfig->getAmountMinVDManu();
                }

                $fdpTypeMargeTotal += $taxeVD;
            }

            if ($shippingConfig->getMarginClientManu() > 0) {
                $fdpTypeMargeTotal += round($fdpTypeMargeTotal * $shippingConfig->getMarginClientManu(), 2);
            }
        }

        if (!$priceGroup->isViewHT()) {
            if ($shippingConfig->getTvaManu() > 0) {
                $fdpManu = ($fdpTypeMargeTotal * (1 + $shippingConfig->getTvaManu())) + $fdpTypeMargeWithoutMargeTotal;
            } else {
                $fdpManu = $fdpTypeMargeTotal + $fdpTypeMargeWithoutMargeTotal;
            }
        } else {
            $fdpManu = $fdpTypeMargeTotal + $fdpTypeMargeWithoutMargeTotal;
        }

        if ($shippingConfig->getAmountByBottleTTC() > 0) {
            $fdpManu += $shippingConfig->getAmountByBottleTTC() * ($quantityAllAbsolute - $equivalence);
        }

        foreach ($quantitiesMinCart as $key => $quantityByType) {
            if (!$country) {
                continue;
            }

            foreach ($country->getZones() as $zone) {
                $shippingAdditional = $this->shippingAdditionalManualRepository->findByQuantityAndByTypeAndByZoneAndByGroup(
                    $quantityByType,
                    $key,
                    $zone,
                    $priceGroupId
                );

                if ($shippingAdditional) {
                    $fdpShippingAdditionnal = $shippingAdditional->getPackagePrice()
                        ?: $shippingAdditional->getBottlePrice() * $quantityByType;

                    if ($shippingConfig->getTvaManu() > 0 && $shippingAdditional->isHt()) {
                        $fdpManu += $fdpShippingAdditionnal * (1 + $shippingConfig->getTvaManu());
                    } else {
                        $fdpManu += $fdpShippingAdditionnal;
                    }

                    break;
                }
            }
        }

        $searchByHm = false;

        if (!$country) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        $shippingSup = $this->shippingAutoSupRepository->findOneBy([
            'country' => $country->getCode(),
            'county' => $county ? $county->getCode() : null,
            'city' => $city,
        ]);

        if ($shippingSup) {
            $searchByHm = true;
        }

        if (\count($quantities) > 0 && $equivalence > 0) {
            $fdp = 0;

            if ($country->isCounty()) {
                if (null === $county) {
                    throw new \Exception($this->translator->trans('shop.error.empty_shipping', [], null, $this->locale));
                }
                $shippingPrice = $this->shippingAutoRepository->findByQuantityAndByCountyAndByCountry(
                    $equivalence,
                    $county->getCode(),
                    $country->getCode(),
                    $searchByHm
                );

                if (!$shippingPrice) {
                    $searchByHm = false;
                    $shippingPrice = $this->shippingAutoRepository->findByQuantityAndByCountyAndByCountry(
                        $equivalence,
                        $county->getCode(),
                        $country->getCode()
                    );
                }
            } else {
                $shippingPrice = $this->shippingAutoRepository->findByQuantityAndByCountry($equivalence, $country->getCode());
            }

            if (!$shippingPrice) {
                throw new \Exception($this->translator->trans('shop.error.missing_shipping', [], null, $this->locale));
            }

            if ($shippingPrice->getBottlePrice()) {
                $fdp += $shippingPrice->getBottlePrice() * $equivalence;
            } else {
                $fdp += $shippingPrice->getPackagePrice();
            }

            if ($shippingConfig->getAmountPercentVDAuto() && $equivalence >= $shippingConfig->getQtyMinVDAuto()) {
                $fdpVD = round(($totalAmount / (1 + $taxDefault->getValue())) * $shippingConfig->getAmountPercentVDAuto(), 2);

                if ($shippingConfig->getAmountMinVDAuto() && $fdpVD < $shippingConfig->getAmountMinVDAuto()) {
                    $fdpVD = $shippingConfig->getAmountMinVDAuto();
                }

                $fdp += $fdpVD;

                if ($shippingConfig->getAmountVDAuto() > 0) {
                    $fdp += $shippingConfig->getAmountVDAuto();
                }
            }

            $fdp += $shippingConfig->getAmountFixedBeforeMargeAuto();
            unset($quantities);

            $shippingSup = $this->shippingAutoSupRepository->findOneBy([
                'country' => $country->getCode(),
                'county' => $county ? $county->getCode() : null,
                'city' => $city,
            ]);

            if ($shippingSup && !$searchByHm) {
                $fdp += $shippingConfig->getHighMountainAmount();
            }

            $fdp = (float) ceil($fdp + ($fdp * $shippingConfig->getMarginAuto()));
            $fdp = (float) ceil($fdp + ($fdp * $shippingConfig->getMarginGazoleAuto()));
            $fdp = (float) ceil($fdp + ($fdp * $shippingConfig->getMarginClientAuto()));
            $fdp += $shippingConfig->getAmountFixedAuto();

            $shippingName = $shippingConfig->getCarrier();
        }

        if (null === $fdp) {
            throw new \Exception($this->translator->trans('shop.error.calcul', [], null, $this->locale));
        }

        if ($country->isCounty()) {
            if (null === $county) {
                throw new \Exception($this->translator->trans('shop.error.empty_shipping', [], null, $this->locale));
            }
            $shippingAdditionals = $this->shippingAdditionalRepository->findByQuantityAndByCountyAndByGroup(
                $equivalence,
                $county->getCode(),
                $priceGroupId
            );

            /** @var ShippingAdditional $shippingAdditional */
            foreach ($shippingAdditionals as $shippingAdditional) {
                $fdp += $shippingAdditional->getPrice();
            }
        }

        if (0.0 === ($fdp + $fdpManu)) {
            $fdpSupp = 0;
        }

        if ($priceGroup->isViewHT()) {
            $shipping = $fdp + $fdpManu + $fdpSupp;

            return ['amount' => $shipping, 'shippingName' => $shippingName];
        }

        $fdpTVA = round($fdp * (1 + $taxDefault->getValue()), 2) + $fdpManu;
        $shipping = $fdpTVA + $fdpSupp;

        if ($shippingConfig->getAmountAutoByBottleTTC() > 0) {
            $shipping = $fdpTVA + $fdpSupp + ($shippingConfig->getAmountAutoByBottleTTC() * $equivalence);
        }

        return ['amount' => $shipping, 'shippingName' => $shippingName];
    }

    private function usesMethodShippingPrices(?DeliveryMethod $deliveryMethod): bool
    {
        return null !== $deliveryMethod
            && $deliveryMethod->isEnabledShippingPrice()
            && $deliveryMethod->getShippingPrices()->count() > 0;
    }
}
