<?php

namespace Myvetshop\Module\Clinique\Mapper\TarifSpecifique;

use Myvetshop\Module\Clinique\Adapter\Factory\SpecificPriceFactory;
use Myvetshop\Module\Clinique\Adapter\Repository\SpecificPriceRepository;
use Myvetshop\Module\Clinique\Entity\MyvetshopTarifSpecifique;

class TarifSpecifiqueToSpecificPriceMapper
{
    private \Db $db;

    private SpecificPriceFactory $specificPriceFactory;

    private SpecificPriceRepository $specificPriceRepository;

    public function __construct(
        \Db $db,
        SpecificPriceFactory $specificPriceFactory,
        SpecificPriceRepository $specificPriceRepository
    ) {
        $this->db = $db;
        $this->specificPriceFactory = $specificPriceFactory;
        $this->specificPriceRepository = $specificPriceRepository;
    }

    /**
     * @return array{delete: list<\SpecificPrice>, insert: list<\SpecificPrice>, update: list<\SpecificPrice>}
     */
    public function buildUpdateQueries(MyvetshopTarifSpecifique $tarif): array
    {
        $specificPrices = $this->specificPriceRepository->findByTarifSpecifique($tarif);

        /** @var array{delete: list<\SpecificPrice>, insert: list<\SpecificPrice>, update: list<\SpecificPrice>} $queries */
        $queries = [
            'delete' => [],
            'insert' => [],
            'update' => [],
        ];

        $actualFardelagePrice = null;

        foreach ($specificPrices as $specificPrice) {
            if ($specificPrice->from_quantity != 2 || $specificPrice->id_group != 0) {
                $queries['delete'][] = $specificPrice;
                continue;
            }

            $actualFardelagePrice = $specificPrice;
        }

        $neededFardelagePrice = $this->toSpecificPrice($tarif);

        /** @var array{delete: list<\SpecificPrice>, insert: list<\SpecificPrice>, update: list<\SpecificPrice>} $queries */
        $queries = \array_merge_recursive(
            $queries,
            $this->prepareUpdateQueries($actualFardelagePrice, $neededFardelagePrice),
        );

        return $queries;
    }

    /**
     * @param array{delete: list<\SpecificPrice>, insert: list<\SpecificPrice>, update: list<\SpecificPrice>} $queries
     */
    public function executeUpdateQueries(array $queries): void
    {
        // Delete query
        if (\count($queries['delete'])) {
            $this->db->execute(
                'DELETE FROM `' . _DB_PREFIX_ . 'specific_price` WHERE `id_specific_price` IN ('
                . \implode(
                    ',',
                    \array_map(
                        fn (\SpecificPrice $specificPrice) => (int) $specificPrice->id,
                        $queries['delete'],
                    )
                )
                . ')'
            );
        }

        // Insert query
        if (\count($queries['insert'])) {
            $this->db->execute(
                'INSERT INTO `' . _DB_PREFIX_ . '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 '
                . \implode(
                    ',',
                    \array_map(
                        fn (\SpecificPrice $specificPrice) => '(0, 0, ' . (int) $specificPrice->id_product . ', '
                            . (int) $specificPrice->id_product_attribute . ', 0, 0, 0, 0, 0, '
                            . (int) $specificPrice->id_group . ', ' . (int) $specificPrice->from_quantity
                            . ', 0, 1, "amount", ' . (float) $specificPrice->price
                            . ', "0000-00-00 00:00:00", "0000-00-00 00:00:00")',
                        $queries['insert'],
                    )
                )
            );
        }

        // Update queries
        if (\count($queries['update'])) {
            foreach ($queries['update'] as $specificPrice) {
                $this->db->execute(
                    'UPDATE `' . _DB_PREFIX_ . 'specific_price` SET price = ' . (float) $specificPrice->price
                    . ' WHERE `id_specific_price` = ' . (int) $specificPrice->id
                );
            }
        }
    }

    /**
     * @param MyvetshopTarifSpecifique $tarif
     * @param list<int> $groupIds
     *
     * @return array<int, array{fardelage: ?\SpecificPrice, privilege: ?\SpecificPrice}> $sortedSpecificPrices
     */
    public function getSortedSpecificPrices(MyvetshopTarifSpecifique $tarif, array $groupIds): array
    {
        $specificPrices = $this->specificPriceRepository->findByTarifSpecifiqueAndGroups(
            $tarif,
            $groupIds
        );

        /** @var array<int, array{fardelage: ?\SpecificPrice, privilege: ?\SpecificPrice}> $sortedSpecificPrices */
        $sortedSpecificPrices = \array_reduce(
            $specificPrices,
            function (array $carry, \SpecificPrice $specificPrice) {
                if (!isset($carry[(int) $specificPrice->id_group])) {
                    $carry[(int) $specificPrice->id_group] = [
                        'fardelage' => null,
                        'privilege' => null,
                    ];
                }

                switch ((int) $specificPrice->from_quantity) {
                    case 1:
                        $key = 'privilege';
                        break;

                    case 2:
                        $key = 'fardelage';
                        break;

                    default:
                        $key = 'unknown';
                }

                $carry[(int) $specificPrice->id_group][$key] = $specificPrice;

                return $carry;
            },
            []
        );

        return $sortedSpecificPrices;
    }

    /**
     * @param ?\SpecificPrice $actualSpecificPrice
     * @param?\SpecificPrice $neededSpecificPrice
     *
     * @return array{delete: list<\SpecificPrice>, insert: list<\SpecificPrice>, update: list<\SpecificPrice>}
     */
    public function prepareUpdateQueries(?\SpecificPrice $actualSpecificPrice, ?\SpecificPrice $neededSpecificPrice): array
    {
        $ret = [
            'delete' => [],
            'insert' => [],
            'update' => [],
        ];

        if ($actualSpecificPrice && !$neededSpecificPrice) {
            $ret['delete'][] = $actualSpecificPrice;
        } elseif (!$actualSpecificPrice && $neededSpecificPrice) {
            $ret['insert'][] = $neededSpecificPrice;
        } elseif (
            $actualSpecificPrice
            && $neededSpecificPrice
            && $actualSpecificPrice->price !== $neededSpecificPrice->price
        ) {
            // Mise à jour du prix
            $actualSpecificPrice->price = $neededSpecificPrice->price;
            $ret['update'][] = $actualSpecificPrice;
        }

        return $ret;
    }

    public function toSpecificPrice(MyvetshopTarifSpecifique $tarif): ?\SpecificPrice
    {
        if ($tarif->tarifFardelage) {
            return $this->specificPriceFactory->create(
                $tarif->idProduct,
                $tarif->idProductAttribute ?? 0,
                2,
                (float) $tarif->tarifFardelage,
            );
        }

        return null;
    }
}
