<?php

namespace KupShop\DeliveryPriceListBundle\Tests;

use KupShop\DeliveryPriceListBundle\DeliveryPriceCalculator;
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
use KupShop\KupShopBundle\Context\CountryContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;

class DeliveryPriceListTest extends \DatabaseTestCase
{
    use CartTestTrait;

    public function setUp(): void
    {
        global $cfg;

        $cfg['Modules']['delivery_pricelist'] = true;

        parent::setUp();

        $this->prepareCart();
    }

    protected function tearDown(): void
    {
        parent::tearDown();

        \Delivery::clearCache();
    }

    public function testMulPrice()
    {
        $price = \Decimal::create(2.22);

        $service = ServiceContainer::getService(DeliveryPriceCalculator::class);
        $service->setPriceMultiplier(2);
        $price = $service->mulPrice($price);

        $this->assertEquals(4.44, $price->asFloat());
    }

    public function testGetPriceWithoutVat()
    {
        $price = \Decimal::create(5.02, 4);

        $service = ServiceContainer::getService(DeliveryPriceCalculator::class);
        $priceWithoutVat = $service->getPriceWithoutVat(21, $price);

        $this->assertEquals(\Decimal::create(4.14876033, 8), $priceWithoutVat);
    }

    public function testGetValueVat()
    {
        $price = \Decimal::create(10.25, 4);

        $service = ServiceContainer::getService(DeliveryPriceCalculator::class);
        $valueVat = $service->getValueVat($service->getPriceWithoutVat(21, $price), $price);

        $this->assertEquals(\Decimal::create(1.77892562, 12), $valueVat);
    }

    public function testGetDeliveryDaysCZ()
    {
        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $days = $this->cart->getDeliveryType()->getDelivery()->getDeliveryDays();

        $this->assertEquals(['min' => 3, 'max' => 3], $days);

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testGetDeliveryDaysSK()
    {
        /** @var CountryContext $context */
        $context = $this->get(CountryContext::class);
        $context->activate('SK');

        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $days = $this->cart->getDeliveryType()->getDelivery()->getDeliveryDays();

        $this->assertEquals(['min' => 3, 'max' => 6], $days);

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testDeliveryPriceFirstWeightGroup()
    {
        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals('200', $price->asFloat());

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testDeliveryPriceThirdWeightGroup()
    {
        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $this->cart->getDeliveryType()->getDelivery()->totalWeight = 2.99;

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals('220', $price->asFloat());

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testDeliveryPriceFifthWeightGroup()
    {
        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $this->cart->getDeliveryType()->getDelivery()->totalWeight = 4.5;

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals('240', $price->asFloat());

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testDeliveryPriceForbidOnMaxWeight()
    {
        $this->insertProduct(1, 18, 1); // weight = 25kg
        $this->cart->createFromDB();

        $this->cart->setTransport(2); // "on_max_weight":"forbid"
        $this->cart->getDeliveryType()->getDelivery()->applyToCart($this->cart);
        $this->assertEquals(25, $this->cart->getDeliveryType()->getDelivery()->totalWeight);

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals(0, $price->asFloat());
        $exception = $this->cart->getDeliveryType()->getDelivery()->exception;
        $this->assertNotNull($exception);
        $this->assertEquals('Váhový limit pro zvolený typ dopravy byl překročen.', $exception->getMessage());
    }

    public function testDeliveryPriceDivideOnMaxItemWeight()
    {
        $this->insertProduct(1, 19, 1); // weight = 14kg
        $this->cart->createFromDB();

        $this->cart->setTransport(1); // "on_max_weight":"divide"
        $this->cart->getDeliveryType()->getDelivery()->applyToCart($this->cart);
        $this->assertEquals(14, $this->cart->getDeliveryType()->getDelivery()->totalWeight);
        $this->assertEquals(14, $this->cart->getDeliveryType()->getDelivery()->maxItemWeight);
        // maxItemWeight > weight_groups.max_weight

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals(0, $price->asFloat());
        $exception = $this->cart->getDeliveryType()->getDelivery()->exception;
        $this->assertNotNull($exception);
        $this->assertEquals('Váhový limit pro zvolený typ dopravy byl překročen.', $exception->getMessage());
    }

    public function testDeliveryPriceDivideOnMaxWeight()
    {
        $this->insertProduct(1, 17, 2); // 2 pcs x 4 kg => divide (weight_groups.max_weight = 5)
        $this->cart->setTransport(1); // "on_max_weight":"divide"
        $this->cart->createFromDB();

        $this->cart->getDeliveryType()->getDelivery()->applyToCart($this->cart);
        $this->assertEquals(8, $this->cart->getDeliveryType()->getDelivery()->totalWeight);
        $this->assertEquals(4, $this->cart->getDeliveryType()->getDelivery()->maxItemWeight);
        // maxItemWeight <= weight_groups.max_weight

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals(460, $price->asFloat()); // 230 * 2

        $this->checkOrderPriceIsSameAsCart();
    }

    /** @dataProvider data_testDeliveryPriceDivideOnMaxWeight2 */
    public function testDeliveryPriceDivideOnMaxWeight2(array $productData, int $expectedTotalWeight, int $expectedMaxItemWeight, float $expectedDeliveryPrice): void
    {
        $this->insertProduct(...$productData); // 4 kg
        $this->cart->setTransport(1); // "on_max_weight":"divide"
        $this->cart->createFromDB();

        $this->cart->getDeliveryType()->getDelivery()->applyToCart($this->cart);

        $this->assertEquals($expectedTotalWeight, $this->cart->getDeliveryType()->getDelivery()->totalWeight);
        $this->assertEquals($expectedMaxItemWeight, $this->cart->getDeliveryType()->getDelivery()->maxItemWeight);
        // maxItemWeight <= weight_groups.max_weight

        $deliveryPrice = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals($expectedDeliveryPrice, $deliveryPrice->asFloat());

        $this->checkOrderPriceIsSameAsCart();
    }

    public function data_testDeliveryPriceDivideOnMaxWeight2(): array
    {
        return [
            [[1, 17, 1], 4, 4, 230],
            [[1, 17, 2], 8, 4, 460],
        ];
    }

    public function testGetCountOfPackages()
    {
        $this->insertProduct(100, null, 2); // 2 pcs
        $this->cart->createFromDB();

        $this->cart->setTransport(1); // "on_max_weight":"divide"
        $this->cart->getDeliveryType()->getDelivery()->applyToCart($this->cart);
        $this->assertEquals(8, $this->cart->getDeliveryType()->getDelivery()->totalWeight);
        $this->assertEquals(4, $this->cart->getDeliveryType()->getDelivery()->maxItemWeight);

        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals(460, $price->asFloat()); // 230 * 2

        $this->checkOrderPriceIsSameAsCart();

        $orderData = $this->order->getData('delivery_data');
        $this->assertEquals(
            [
                'package_weight' => 4,
                'package_count' => 2,
            ],
            $orderData);
    }

    public function testDeliveryPrice()
    {
        $this->insertProduct(1, 16);
        $this->cart->createFromDB();

        $this->cart->setTransport(1);
        $price = $this->cart->getDeliveryType()->getDelivery()->getPrice()->getPriceWithVat();

        $this->assertEquals(200, $price->asFloat());

        $this->checkOrderPriceIsSameAsCart();
    }

    public function testOrderProcessUpdateAddresses()
    {
        /** @var CurrencyContext $context */
        $context = $this->get(CurrencyContext::class);
        $context->activate('EUR');

        $this->insertProduct(1, 16);

        $this->cart->setTransport(1);
        $this->cart->delivery_id = 1;

        $invoice = [
            'email' => 'emal@email.cz',
            'name' => 'test',
            'surname' => 'test',
            'phone' => '123456789',
            'street' => 'ulice 12',
            'city' => 'Mesto',
            'zip' => '12345',
            'country' => 'SK',
        ];

        $error = $this->cart->updateAddresses($invoice, []);

        $this->assertEquals(null, $error);
    }

    public function getDataSet()
    {
        return $this->getJsonDataSetFromFile();
    }
}
