<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (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\Shop;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use CpCreation\VitiCore\Product\Repository\ProductTypeRepository;
use CpCreation\VitiCore\Shop\DTO\ShippingContext;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final class ShippingContextFactory
{
    public function __construct(
        private RequestStack $requestStack,
        private ProductPriceGroupRepository $productPriceGroupRepository,
        private ProductTypeRepository $productTypeRepository,
        private TranslatorInterface $translator,
    ) {
    }

    /**
     * @throws \Exception
     */
    public function createFromCart(Cart $cart): ShippingContext
    {
        $session = $this->getSession();

        if (null === $session) {
            throw new \Exception($this->translator->trans('shop.error.calcul'));
        }

        $priceGroupId = GroupContext::$groupId;
        $codePriceId = $session->get('codePrice');

        if (!\is_string($priceGroupId) || '' === $priceGroupId) {
            throw new \Exception($this->translator->trans('shop.error.calcul'));
        }

        $priceGroup = $this->productPriceGroupRepository->find($priceGroupId);

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

        $typeDefault = $this->productTypeRepository->findDefaultType();

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

        [$countryCode, $postalCode, $city] = $this->extractDestination($cart);

        $itemsTotalTTC = 0;
        $quantityAllAbsolute = 0.0;
        $equivalenceAuto = 0.0;
        $equivalenceDefault = 0.0;
        $equivalenceOff = 0;

        $typeDefaultSlug = $typeDefault->getSlug();

        /** @var array<string, float> $quantities */
        $quantities = [
            $typeDefaultSlug => 0.0,
        ];

        /** @var array<string, float> $quantitiesManual */
        $quantitiesManual = [
            $typeDefaultSlug => 0.0,
        ];

        /** @var array<string, float> $quantitiesAutoFallback */
        $quantitiesAutoFallback = [
            $typeDefaultSlug => 0.0,
        ];

        /** @var array<string, float> $quantitiesMinCart */
        $quantitiesMinCart = [
            $typeDefaultSlug => 0.0,
        ];

        /** @var array<string, bool> $typeSeen */
        $typeSeen = [
            $typeDefaultSlug => true,
        ];

        /** @var list<string> $typeSlugs */
        $typeSlugs = [
            $typeDefaultSlug,
        ];

        /** @var array<string, string> $typeNames */
        $typeNames = [
            $typeDefaultSlug => $typeDefault->getName(),
        ];

        /** @var array<string, bool> $freeShippingWithBottle */
        $freeShippingWithBottle = [
            $typeDefaultSlug => false,
        ];

        /** @var array<string, int|null> $minFreeShippingWithBottle */
        $minFreeShippingWithBottle = [
            $typeDefaultSlug => null,
        ];

        /** @var array<string, list<string>> $zonesFreeShippingWithBottle */
        $zonesFreeShippingWithBottle = [
            $typeDefaultSlug => [],
        ];

        /** @var array<string, bool> $disabledShippingAuto */
        $disabledShippingAuto = [
            $typeDefaultSlug => false,
        ];

        /** @var array<string, bool> $disabledShippingManu */
        $disabledShippingManu = [
            $typeDefaultSlug => false,
        ];

        /** @var CartItem $item */
        foreach ($cart->getItems() as $item) {
            $variant = $item->getVariant();

            if (null === $variant) {
                continue;
            }

            $type = $variant->getType();
            $typeSlug = $type->getSlug();
            $quantity = $item->getQuantity();

            $equivalency = $type->getEquivalency() ?? 0.0;
            $equivalencyManu = $type->getEquivalencyManu() ?? 0.0;

            $itemsTotalTTC += $item->getTotalTTC();
            $equivalenceOff += $quantity;

            if (!isset($typeSeen[$typeSlug])) {
                $typeSeen[$typeSlug] = true;
                $typeSlugs[] = $typeSlug;
            }

            if (!isset($quantities[$typeSlug])) {
                $quantities[$typeSlug] = 0.0;
            }

            if (!isset($quantitiesManual[$typeSlug])) {
                $quantitiesManual[$typeSlug] = 0.0;
            }

            if (!isset($quantitiesAutoFallback[$typeSlug])) {
                $quantitiesAutoFallback[$typeSlug] = 0.0;
            }

            if (!isset($quantitiesMinCart[$typeSlug])) {
                $quantitiesMinCart[$typeSlug] = 0.0;
            }

            if (!isset($typeNames[$typeSlug])) {
                $typeNames[$typeSlug] = $type->getName();
            }

            if (!isset($freeShippingWithBottle[$typeSlug])) {
                $freeShippingWithBottle[$typeSlug] = $type->isFreeShippingWithBottle();
            }

            if (!isset($minFreeShippingWithBottle[$typeSlug])) {
                $minFreeShippingWithBottle[$typeSlug] = $type->isFreeShippingWithBottle()
                    ? $type->getMinFreeShippingWithBottle()
                    : null;
            }

            if (!isset($zonesFreeShippingWithBottle[$typeSlug])) {
                /** @var list<string> $zones */
                $zones = $type->getZonesFreeShippingId();
                $zonesFreeShippingWithBottle[$typeSlug] = $zones;
            }

            if (!isset($disabledShippingAuto[$typeSlug])) {
                $disabledShippingAuto[$typeSlug] = $type->isDisabledShippingAuto();
            }

            if (!isset($disabledShippingManu[$typeSlug])) {
                $disabledShippingManu[$typeSlug] = $type->isDisabledShippingManu();
            }

            if (!$type->isAccessory()) {
                $quantityAllAbsolute += $quantity * $equivalency;
            }

            if (!$type->isDisabledShippingAuto() && !$type->isAccessory()) {
                $equivalenceAuto += $quantity * $equivalency;
            }

            if ($type->isEquivalencyOnShipping()) {
                $equivalenceDefault += $quantity * $equivalencyManu;
            }

            if ($type->isEquivalencyDefault()) {
                $equivalenceDefault += $quantity * $equivalencyManu;
            }

            if ($type->isEquivalencyOnShipping()) {
                $quantities[$typeDefaultSlug] += $quantity * $equivalency;
                $quantitiesManual[$typeDefaultSlug] += $quantity * $equivalencyManu;
                $quantitiesAutoFallback[$typeDefaultSlug] += $quantity * $equivalency;
            } else {
                $quantities[$typeSlug] += $quantity;
                $quantitiesManual[$typeSlug] += $quantity;
                $quantitiesAutoFallback[$typeSlug] += $quantity * $equivalency;
            }

            if ($type->isEquivalencyDefaultQuantity()) {
                $quantitiesMinCart[$typeDefaultSlug] += $quantity * $equivalency;
            } else {
                $quantitiesMinCart[$typeSlug] += $quantity;
            }
        }

        return new ShippingContext(
            cart: $cart,
            priceGroupId: $priceGroupId,
            codePriceId: \is_string($codePriceId) && '' !== $codePriceId ? $codePriceId : null,
            countryCode: $countryCode,
            postalCode: $postalCode,
            city: $city,
            deliveryMethod: $cart->getDeliveryMethod(),
            itemsTotalTTC: $itemsTotalTTC,
            quantityAllAbsolute: $quantityAllAbsolute,
            equivalenceAuto: $equivalenceAuto,
            equivalenceDefault: $equivalenceDefault,
            equivalenceOff: $equivalenceOff,
            quantities: $quantities,
            quantitiesManual: $quantitiesManual,
            quantitiesAutoFallback: $quantitiesAutoFallback,
            quantitiesMinCart: $quantitiesMinCart,
            typeSlugs: $typeSlugs,
            typeDefaultSlug: $typeDefaultSlug,
            typeNames: $typeNames,
            freeShippingWithBottle: $freeShippingWithBottle,
            minFreeShippingWithBottle: $minFreeShippingWithBottle,
            zonesFreeShippingWithBottle: $zonesFreeShippingWithBottle,
            disabledShippingAuto: $disabledShippingAuto,
            disabledShippingManu: $disabledShippingManu,
        );
    }

    /**
     * @return array{0: ?string, 1: ?string, 2: ?string}
     */
    private function extractDestination(Cart $cart): array
    {
        $deliveryAddress = $cart->getDeliveryAddress();

        return [
            $deliveryAddress?->getCountry(),
            $deliveryAddress?->getPostal(),
            $deliveryAddress?->getCity(),
        ];
    }

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