<?php

declare(strict_types=1);

namespace External\ZNZBundle\Overrides\Ordering;

use External\ZNZBundle\Util\Product\ZNZProductUtil;
use External\ZNZBundle\Util\ZNZConfiguration;
use KupShop\CatalogBundle\ProductList\ProductCollection;
use KupShop\StoresBundle\Utils\StoresInStore;
use Symfony\Contracts\Service\Attribute\Required;

class DeliveryInfo extends \KupShop\OrderingBundle\Util\Order\DeliveryInfo
{
    #[Required]
    public StoresInStore $storesInStore;
    #[Required]
    public ZNZProductUtil $productUtil;
    #[Required]
    public ZNZConfiguration $configuration;

    protected function calculateCollectionDate(ProductCollection $productList, \Delivery $delivery, array &$cache): ?\DateTime
    {
        $deliveryDate = parent::calculateCollectionDate($productList, $delivery, $cache);

        if ($deliveryDate && $delivery->isInPerson() && ($cache['inPersonMinInStore'] ?? false) !== false) {
            if ($cache['inPersonMinInStore'] <= 0) {
                $deliveryDate->add(new \DateInterval('P1D'));
            }
        } else {
            if ($cache['supplierOrderingDate'] ?? null) {
                $deliveryDate = clone $cache['supplierOrderingDate'];
            }

            if ($days = $this->configuration->getExpeditionClosedDays()) {
                $deliveryDate->add(new \DateInterval('P'.$days.'D'));
            }
        }

        $delivery->_znzFromDay = $deliveryDate;

        return $deliveryDate;
    }

    protected function calculateDeliveryDays(\Delivery $delivery): int
    {
        return $delivery->getDeliveryDays($delivery->_znzFromDay ?? null)['min'] ?? 0;
    }

    protected function calculateInitialCache(ProductCollection $productList): array
    {
        if (!$productList->count()) {
            return [];
        }

        $productList->fetchStoresInStore();

        $stores = array_keys(array_filter($this->storesInStore->getStores(), fn ($x) => !empty($x['id_delivery'])));

        $minInStore = null;
        foreach ($productList as $product) {
            foreach ($stores as $storeId) {
                $inStore = (float) ($product->storesInStore[$storeId]['in_store'] ?? 0);
                $minInStore = $minInStore ? min($inStore, $minInStore) : $inStore;
            }
        }

        return [
            'inPersonMinInStore' => $minInStore,
            'supplierOrderingDate' => $this->calculateSupplierOrderDate($productList),
        ];
    }

    private function calculateSupplierOrderDate(ProductCollection $products): ?\DateTime
    {
        $today = new \DateTime();
        $todayIndex = (int) $today->format('N');
        $todayHour = (int) $today->format('H');
        $todayMinute = (int) $today->format('i');

        $this->productUtil->fetchZNZSuppliersInfo($products);

        $dates = [];
        // spocitam nejblizsi mozny datum dodani pokud je produkt potreba objednat od dodavatele
        foreach ($products as $product) {
            // pokud nema zadny data o dodavateli, tak skipuju
            if (empty($product->znz_suppliers_info)) {
                continue;
            }

            foreach ($product->znz_suppliers_info as $info) {
                if (empty($info)) {
                    continue;
                }

                $days = explode(',', $info['dnyVTydnu']);
                [$hour, $minute, $_] = array_map('intval', explode(':', $info['prahObjednani']));

                $orderingToday = false;

                // zkontroluju, zda se stihne objednat jeste dnes
                if (in_array($todayIndex, $days) && ($todayHour < $hour || ($todayHour == $hour && $todayMinute <= $minute))) {
                    $orderingToday = true;
                }

                // najdu dalsi nejblizsi den, kdy se bude objedavat
                $nextOrderDate = clone $today;
                if (!$orderingToday) {
                    $nextOrderDate = $this->getNextOrderDate($days);
                }

                $minutes = $info['dobaDodani'] * 60;
                $dates[] = $nextOrderDate->modify("+{$minutes} minutes");
            }
        }

        if (!empty($dates)) {
            return max($dates);
        }

        return null;
    }

    private function getNextOrderDate(array $days): \DateTime
    {
        // Get the current day (1 for Monday, 7 for Sunday)
        $currentDay = date('N');
        // Sort days to ensure they are in order
        sort($days);

        // Create a DateTime object for "now"
        $date = (new \DateTime())->setTime(8, 0);

        foreach ($days as $day) {
            if ($day > $currentDay) {
                // Calculate the difference in days
                $dayDiff = $day - $currentDay;
                // Modify the DateTime object to the next closest day
                $date->modify("+{$dayDiff} days");

                return $date;
            }
        }

        // If the next day is not found in the current week, find the first day in the next week
        $dayDiff = ($days[0] + 7) - $currentDay;
        $date->modify("+{$dayDiff} days");

        return $date;
    }
}
