<?php

namespace App\Tests\A_Unit\Mapper\TarifSpecifique;

use Myvetshop\Module\Clinique\Adapter\Factory\SpecificPriceFactory;
use Myvetshop\Module\Clinique\Adapter\Repository\SpecificPriceRepository;
use Myvetshop\Module\Clinique\Entity\MyvetshopTarifSpecifique;
use Myvetshop\Module\Clinique\Mapper\TarifSpecifique\TarifSpecifiqueToSpecificPriceMapper;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class TarifSpecifiqueToSpecificPriceMapperTest extends TestCase
{
    /**
     * @var \Db&MockObject
     */
    private \Db $db;

    /**
     * @var SpecificPriceFactory&MockObject
     */
    private SpecificPriceFactory $specificPriceFactory;

    /**
     * @var SpecificPriceRepository&MockObject
     */
    private SpecificPriceRepository $specificPriceRepository;

    private TarifSpecifiqueToSpecificPriceMapper $mapper;

    protected function setUp(): void
    {
        $this->db = $this->createMock(\Db::class);
        $this->specificPriceFactory = $this->getMockBuilder(SpecificPriceFactory::class)
            ->onlyMethods(['createEntity'])
            ->getMock();
        $this->specificPriceFactory
            ->method('createEntity')
            ->willReturnCallback(function () {
                return $this->createMock(\SpecificPrice::class);
            });
        $this->specificPriceRepository = $this->createMock(SpecificPriceRepository::class);

        $this->mapper = new TarifSpecifiqueToSpecificPriceMapper(
            $this->db,
            $this->specificPriceFactory,
            $this->specificPriceRepository,
        );
    }

    public function testBuildUpdateQueries(): void
    {
        $tarif = new MyvetshopTarifSpecifique();

        $tarif->tarifFardelage = '10.50';
        $tarif->tarifPrivilege = null;
        $tarif->idProduct = 1;
        $tarif->idProductAttribute = 2;

        $queries = $this->mapper->buildUpdateQueries($tarif);

        $this->assertCount(1, $queries['insert']);
        $this->assertEquals(1, $queries['insert'][0]->id_product);
        $this->assertEquals(2, $queries['insert'][0]->id_product_attribute);
        $this->assertEquals(0, $queries['insert'][0]->id_group);
        $this->assertEquals(2, $queries['insert'][0]->from_quantity);
        $this->assertEquals(10.50, $queries['insert'][0]->price);
    }

    public function testToSpecificPrice(): void
    {
        $tarif = new MyvetshopTarifSpecifique();

        $tarif->tarifFardelage = '10.50';
        $tarif->tarifPrivilege = null;
        $tarif->idProduct = 1;
        $tarif->idProductAttribute = 1;

        $ret = $this->mapper->toSpecificPrice($tarif);

        $this->assertNotNull($ret);

        // Le montant doit prendre en compte la réduction de groupe
        $this->assertEquals(2, $ret->from_quantity);
        $this->assertEquals(10.50, $ret->price);
    }

    public function testExecuteUpdateQueries(): void
    {
        $specificPriceI = $this->createMock(\SpecificPrice::class);
        $specificPriceI->id_product = 10;
        $specificPriceI->id_product_attribute = 11;
        $specificPriceI->id_group = 2;
        $specificPriceI->price = '10.00';
        $specificPriceD = $this->createMock(\SpecificPrice::class);
        $specificPriceD->id = 1;
        $specificPriceU = $this->createMock(\SpecificPrice::class);
        $specificPriceU->id = 12;
        $specificPriceU->price = '11.00';

        $queries = [
            'delete' => [$specificPriceD],
            'insert' => [$specificPriceI],
            'update' => [$specificPriceU],
        ];

        $matcher = $this->exactly(3);
        $this->db
            ->expects($matcher)
            ->method('execute')
            ->willReturnCallback(function (string $query) use ($matcher) {
                switch ($matcher->getInvocationCount()) {
                    case 1:
                        $this->assertEquals('DELETE FROM `pstest_specific_price` WHERE `id_specific_price` IN (1)', $query);
                        break;

                    case 2:
                        $this->assertEquals('INSERT INTO `pstest_specific_price` (`id_specific_price_rule`, `id_cart`, `id_product`, `id_product_attribute`, `id_shop`, `id_shop_group`, `id_currency`, `id_country`, `id_customer`, `id_group`, `from_quantity`, `reduction`, `reduction_tax`, `reduction_type`, `price`, `from`, `to`) VALUES (0, 0, 10, 11, 0, 0, 0, 0, 0, 2, 0, 0, 1, "amount", 10, "0000-00-00 00:00:00", "0000-00-00 00:00:00")', $query);
                        break;

                    case 3:
                        $this->assertEquals('UPDATE `pstest_specific_price` SET price = 11 WHERE `id_specific_price` = 12', $query);
                        break;
                }
            });

        $this->mapper->executeUpdateQueries($queries);
    }

    public function testGetSortedSpecificPrices(): void
    {
        $tarif = new MyvetshopTarifSpecifique();

        $tarif->tarifFardelage = '10.50';
        $tarif->tarifPrivilege = '10.00';
        $tarif->idProduct = 1;
        $tarif->idProductAttribute = 1;

        $specificPrice1 = $this->createMock(\SpecificPrice::class);
        $specificPrice1->id_group = 1;
        $specificPrice1->from_quantity = 1;
        $specificPrice2 = $this->createMock(\SpecificPrice::class);
        $specificPrice2->id_group = 1;
        $specificPrice2->from_quantity = 2;
        $specificPrice3 = $this->createMock(\SpecificPrice::class);
        $specificPrice3->id_group = 2;
        $specificPrice3->from_quantity = 2;

        $this->specificPriceRepository
            ->method('findByTarifSpecifiqueAndGroups')
            ->with($tarif, [1, 2])
            ->willReturn([
                $specificPrice1,
                $specificPrice2,
                $specificPrice3,
            ]);

        $sortedSpecificPrices = $this->mapper->getSortedSpecificPrices($tarif, [1, 2]);

        $this->assertSame(
            [
                1 => [
                    'fardelage' => $specificPrice2,
                    'privilege' => $specificPrice1,
                ],
                2 => [
                    'fardelage' => $specificPrice3,
                    'privilege' => null,
                ],
            ],
            $sortedSpecificPrices
        );
    }

    public function testPrepareUpdateQueries(): void
    {
        $specificPrice1 = $this->createMock(\SpecificPrice::class);
        $specificPrice1->price = '10.00';
        $specificPrice2 = $this->createMock(\SpecificPrice::class);
        $specificPrice2->price = '11.00';

        $queries = $this->mapper->prepareUpdateQueries($specificPrice1, $specificPrice2);

        $this->assertSame(
            [
                'delete' => [],
                'insert' => [],
                'update' => [$specificPrice1],
            ],
            $queries
        );
        $this->assertEquals($specificPrice1->price, $specificPrice2->price);
    }

    public function testPrepareUpdateQueriesDelete(): void
    {
        $specificPrice1 = $this->createMock(\SpecificPrice::class);
        $specificPrice1->price = '10.00';

        $queries = $this->mapper->prepareUpdateQueries($specificPrice1, null);

        $this->assertSame(
            [
                'delete' => [$specificPrice1],
                'insert' => [],
                'update' => [],
            ],
            $queries
        );
    }
}
