<?php

declare(strict_types=1);

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

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\CalculShipping;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Model\DeliveryAddress;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Product\Model\ProductPrice;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Model\ProductType;
use CpCreation\VitiCore\Product\Model\ProductVariant;
use CpCreation\VitiCore\Product\Repository\ProductPriceCodeRepository;
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\ShippingConfig;
use CpCreation\VitiCore\Shop\Model\ShippingCountry;
use CpCreation\VitiCore\Shop\Model\ShippingCounty;
use CpCreation\VitiCore\Shop\Model\ShippingPrice;
use CpCreation\VitiCore\Shop\Model\ShippingZone;
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 Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final class CalculShippingTest extends TestCase
{
    public function testSpecificDeliveryMethodWithInvalidCountyReportsCountyError(): void
    {
        $group = new ProductPriceGroup();
        $groupId = $group->getId()->toString();

        $type = $this->createProductType();
        $variant = $this->createProductVariant($type, $group);
        $item = $this->createCartItem($variant);

        $allowedCounty = new ShippingCounty();
        $allowedCounty->setCode('51');

        $selectedCounty = new ShippingCounty();
        $selectedCounty->setCode('75');
        $selectedCounty->setZone(new ShippingZone());

        $deliveryMethod = $this->createDeliveryMethod($allowedCounty);
        $cart = $this->createCart($deliveryMethod, $item);

        $calculator = $this->createCalculator($groupId, $group, $type, $selectedCounty);

        $this->expectException(\Exception::class);
        $this->expectExceptionMessage('COUNTY_ERROR');

        $calculator->shippingTotalPrice($cart);
    }

    private function createProductType(): ProductType
    {
        $type = $this->getMockBuilder(ProductType::class)
            ->disableOriginalConstructor()
            ->onlyMethods([
                'getMaximumQuantityCart',
                'getEquivalencyManu',
                'getEquivalency',
                'getEquivalencyFreeShipping',
                'isEquivalencyDefault',
                'isEquivalencyOnShipping',
                'isEquivalencyOnFreeShipping',
                'isEquivalencyDefaultQuantity',
                'getMultipleQuantityCart',
                'isFreeShippingWithBottle',
                'isDisabledShippingManu',
                'isDisabledShippingAuto',
                'isAccessory',
                'getOrderWithaMinimumEquivalency',
                'getZonesFreeShippingId',
            ])
            ->addMethods(['getSlug', 'getName'])
            ->getMock();

        $type->method('getSlug')->willReturn('bottle');
        $type->method('getName')->willReturn('Bouteille');
        $type->method('getMaximumQuantityCart')->willReturn(0);
        $type->method('getEquivalencyManu')->willReturn(1.0);
        $type->method('getEquivalency')->willReturn(1.0);
        $type->method('getEquivalencyFreeShipping')->willReturn(1.0);
        $type->method('isEquivalencyDefault')->willReturn(false);
        $type->method('isEquivalencyOnShipping')->willReturn(true);
        $type->method('isEquivalencyOnFreeShipping')->willReturn(false);
        $type->method('isEquivalencyDefaultQuantity')->willReturn(true);
        $type->method('getMultipleQuantityCart')->willReturn(1);
        $type->method('isFreeShippingWithBottle')->willReturn(false);
        $type->method('isDisabledShippingManu')->willReturn(false);
        $type->method('isDisabledShippingAuto')->willReturn(false);
        $type->method('isAccessory')->willReturn(false);
        $type->method('getOrderWithaMinimumEquivalency')->willReturn(null);
        $type->method('getZonesFreeShippingId')->willReturn([]);

        return $type;
    }

    private function createProductVariant(ProductType $type, ProductPriceGroup $group): ProductVariant
    {
        $price = $this->createMock(ProductPrice::class);
        $price->method('getGroup')->willReturn($group);
        $price->method('getPriceDiscount')->willReturn(null);
        $price->method('getPrice')->willReturn(100);

        $variant = $this->createMock(ProductVariant::class);
        $variant->method('getPrices')->willReturn(new ArrayCollection([$price]));
        $variant->method('getType')->willReturn($type);
        $variant->method('getStock')->willReturn(999);

        return $variant;
    }

    private function createCartItem(ProductVariant $variant): CartItem
    {
        $item = $this->createMock(CartItem::class);
        $item->method('getVariant')->willReturn($variant);
        $item->method('getQuantity')->willReturn(6);

        return $item;
    }

    private function createDeliveryMethod(ShippingCounty $allowedCounty): DeliveryMethod
    {
        $deliveryMethod = $this->createMock(DeliveryMethod::class);
        $deliveryMethod->method('isMultipleVariant')->willReturn(false);
        $deliveryMethod->method('getProductVariants')->willReturn(new ArrayCollection());
        $deliveryMethod->method('getShippingPrices')->willReturn(new ArrayCollection([new ShippingPrice()]));
        $deliveryMethod->method('getCounties')->willReturn(new ArrayCollection([$allowedCounty]));
        $deliveryMethod->method('isViewCounties')->willReturn(true);
        $deliveryMethod->method('getCategory')->willReturn(null);

        return $deliveryMethod;
    }

    private function createCart(DeliveryMethod $deliveryMethod, CartItem $item): Cart
    {
        $deliveryAddress = $this->createMock(DeliveryAddress::class);
        $deliveryAddress->method('isDifferentAddress')->willReturn(true);
        $deliveryAddress->method('getPostalCode')->willReturn('75000');
        $deliveryAddress->method('getCity')->willReturn('Paris');
        $deliveryAddress->method('getCountry')->willReturn('FR');

        $cart = $this->createMock(Cart::class);
        $cart->method('getItems')->willReturn(new ArrayCollection([$item]));
        $cart->method('getDeliveryMethod')->willReturn($deliveryMethod);
        $cart->method('getDeliveryAddress')->willReturn($deliveryAddress);

        return $cart;
    }

    private function createCalculator(
        string $groupId,
        ProductPriceGroup $group,
        ProductType $type,
        ShippingCounty $selectedCounty
    ): CalculShipping {
        $session = $this->createMock(SessionInterface::class);
        $session->method('get')->willReturnCallback(static function (string $name, $default = null) use ($groupId) {
            if ('_locale' === $name) {
                return 'fr';
            }
            if ('groupPrice' === $name) {
                return $groupId;
            }

            return $default;
        });

        $translator = $this->createMock(TranslatorInterface::class);
        $translator->method('trans')->willReturnCallback(static function (string $id) {
            if ('shop.delivery_method.error_county' === $id) {
                return 'COUNTY_ERROR';
            }
            if ('shop.error.missing_shipping_delivery_method' === $id) {
                return 'QUANTITY_ERROR';
            }

            return $id;
        });

        $productPriceGroupRepository = $this->createMock(ProductPriceGroupRepository::class);
        $productPriceGroupRepository->method('find')->with($groupId)->willReturn($group);

        $shippingConfig = $this->createMock(ShippingConfig::class);
        $shippingConfig->method('getAmountFreeShipping')->willReturn(null);
        $shippingConfig->method('getAmountMarginBank')->willReturn(0);
        $shippingConfig->method('getMarginBank')->willReturn(0.0);
        $shippingConfig->method('getQtyEquivalencyBottle')->willReturn(null);
        $shippingConfig->method('isForceEquivalencyForAuto')->willReturn(false);

        $shippingConfigRepository = $this->createMock(ShippingConfigRepository::class);
        $shippingConfigRepository->method('findOne')->willReturn($shippingConfig);

        $productTypeRepository = $this->createMock(ProductTypeRepository::class);
        $productTypeRepository->method('findDefaultType')->willReturn($type);

        $cartRepository = $this->createMock(CartRepository::class);
        $cartRepository->method('stockSaleSinceStockAt')->willReturn([['quantitySale' => 0]]);

        $country = $this->createMock(ShippingCountry::class);
        $country->method('isCounty')->willReturn(true);
        $country->method('getCode')->willReturn('FR');

        $shippingCountryRepository = $this->createMock(ShippingCountryRepository::class);
        $shippingCountryRepository->method('findOneByGroup')->with('FR', $groupId)->willReturn($country);

        $shippingCountyRepository = $this->createMock(ShippingCountyRepository::class);
        $shippingCountyRepository->method('findDepartmentByCountry')->with('75', 'FR')->willReturn($selectedCounty);

        $shippingZoneRepository = $this->createMock(ShippingZoneRepository::class);
        $shippingZoneRepository->method('findByCountryAndByCounty')->willReturn($selectedCounty->getZone());

        $shopConfigMultipleRepository = $this->createMock(ShopConfigMultipleRepository::class);
        $shopConfigMultipleRepository->method('findByQuantityAndByTypeAndByZone')->willReturn(null);

        $shippingPriceRepository = $this->createMock(ShippingPriceRepository::class);
        $shippingPriceRepository->method('findByQuantityAndByTypeAndByZoneAndByGroupAndByPriceCode')->willReturn(null);

        return new CalculShipping(
            $productPriceGroupRepository,
            $this->createMock(ProductPriceCodeRepository::class),
            $shippingConfigRepository,
            $productTypeRepository,
            $cartRepository,
            $this->createMock(ProductTaxRepository::class),
            $shippingCountryRepository,
            $shippingCountyRepository,
            $shippingPriceRepository,
            $this->createMock(ShippingAutoRepository::class),
            $this->createMock(ShippingAutoSupRepository::class),
            $this->createMock(ShippingAdditionalManualRepository::class),
            $this->createMock(ShippingAdditionalRepository::class),
            $shippingZoneRepository,
            $shopConfigMultipleRepository,
            $translator,
            new RequestStack(),
            $session
        );
    }
}
