<?php

namespace KupShop\CatalogBundle\ProductList;

use KupShop\ContentBundle\Util\SliderUtil;
use KupShop\KupShopBundle\Context\CacheContext;
use KupShop\KupShopBundle\Pager\PagerExtraItem;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;

class ProductListBanners
{
    /** @var int Number of products between slides */
    protected $stepSize = 50;

    protected $count;

    /** @var int ID of slider */
    protected $sliderId;

    /** @var int seed to distinguish each instance */
    protected $seed;

    /** @var ?int Aby se bannery zobrazovaly někde nahoře v sekci, ne až úplně dole */
    protected ?int $maxPosition = null;

    /** @var ?int Aby se bannery nezobrazovaly někde v popředí (například u top produktu) */
    protected ?int $minPosition = null;

    protected SliderUtil $sliderUtil;

    public function __construct(SliderUtil $sliderUtil)
    {
        $this->sliderUtil = $sliderUtil;
    }

    public function getPagerExtraItem(\Pager $pager): ?PagerExtraItem
    {
        if ($this->getSlidesCount() <= 0) {
            return null;
        }

        $slideCountOnFirstPage = $this->getSlidesCounts(0, $pager->getOnPage());

        return new PagerExtraItem($slideCountOnFirstPage, $this->count);
    }

    /**
     * Attaches a resultModifier to $productList which assigns `banner` member to
     * products.
     */
    public function attachToProductList(\ProductList $productList, \Pager $pager): void
    {
        $slideFrom = $this->getSkippedProductsCount($pager->from());
        $slideTo = $this->getSkippedProductsCount($pager->to() + 1);
        $slideCount = (int) ($slideTo - $slideFrom);

        if (!$slideCount) {
            return;
        }

        $slides = $this->getSlides();
        if (!$slides) {
            return;
        }

        $slideFrom = $this->getSkippedProductsCount($pager->from());
        $slideTo = $this->getSkippedProductsCount($pager->to() + 1);
        $slideCount = $slideTo - $slideFrom;

        // Assign banners to products
        $productList->addResultModifiers(function (ProductCollection $products) use ($slideFrom, $slideCount, $slides) {
            if ($this->stepSize > $products->count()) {
                // if products count is lower than stepSize - add just one slide
                $this->setStepSize($products->count());
                $slideCount = 1;
            }

            foreach (range(0, $slideCount - 1) as $index) {
                $slideIndex = $slideFrom + $index;
                $bannerPosition = $this->getBannerPosition($slideIndex);
                $slide = $slides[$slideIndex % count($slides)];
                $productPosition = $index * $this->stepSize + max(0, $bannerPosition - $index);
                $productPosition = max(1, $productPosition);

                // don't allow slide on last position
                if ($products->count() && $productPosition == $products->count()) {
                    $productPosition--;
                    while (($productPosition > 0) && (!empty($products->getValues()[$productPosition]->banner))) {
                        $productPosition--;
                    }
                }

                if ($productPosition < $products->count()) {
                    $products->getValues()[$productPosition]->banner = $slide;
                }
            }
        });
    }

    public function attach(\ProductList $productList, \Pager $pager)
    {
        if ($this->getSlidesCount() <= 0) {
            $this->setCount(0);
            // Set pagination/product count without subtracted slideCount
            $productList->limit($pager->onPage, $pager->from());

            return;
        }

        if ($extraItem = $this->getPagerExtraItem($pager)) {
            $pager->addExtraItems($extraItem);
        }

        $this->attachToProductList($productList, $pager);
    }

    public function setSeed(int $seed): ProductListBanners
    {
        $this->seed = $seed;

        return $this;
    }

    protected function getBannerPosition($bannerIndex)
    {
        srand($bannerIndex + $this->seed * 123456);

        $maxPosition = $this->maxPosition ?: $this->stepSize - 1;

        return rand(min($this->minPosition ?: 0, $maxPosition), $maxPosition); // -1 to disable selection of last position
    }

    public function setStepSize(int $stepSize): ProductListBanners
    {
        $this->stepSize = $stepSize;

        return $this;
    }

    /**
     * @return $this
     */
    public function setCount(int $count)
    {
        $this->count = $count;

        return $this;
    }

    public function getCount()
    {
        return $this->count;
    }

    public function setSliderId(int $sliderId): ProductListBanners
    {
        $this->sliderId = $sliderId;

        return $this;
    }

    protected function getSlidesCount(): int
    {
        $cacheContext = ServiceContainer::getService(CacheContext::class);

        $sliderKey = implode('-', [
            $this->sliderId,
            $cacheContext->getKey([
                CacheContext::TYPE_TEXT,
                CacheContext::TYPE_FULL_PAGE,
            ]),
        ]);

        $sliderCounts = getCache('sliders-catalog-slidesCounts');
        if (($sliderCounts[$sliderKey] ?? null) === null) {
            $sliderCounts[$sliderKey] = count($this->getSlides());
            setCache('sliders-catalog-slidesCounts', $sliderCounts, 1800);
        }

        return $sliderCounts[$sliderKey] ?? 0;
    }

    protected function getSlides(): array
    {
        if (!($slider = $this->sliderUtil->get((int) $this->sliderId))) {
            return [];
        }

        return $slider->images;
    }

    protected function getSlidesCounts(int $from, int $to): int
    {
        $slideFrom = $this->getSkippedProductsCount($from);
        $slideTo = $this->getSkippedProductsCount($to);

        return $slideTo - $slideFrom;
    }

    /**
     * @return float|int
     */
    public function getSkippedProductsCount($from)
    {
        $count = intdiv($from, $this->stepSize);
        $remaining = $from % $this->stepSize;

        if ($this->getBannerPosition($count) < $remaining) {
            $count++;
        }

        if ($this->count) {
            return min($count, $this->count);
        }

        return $count;
    }

    /**
     * @return $this
     */
    public function setSeedFromFilterParams(\FilterParams $filterParams)
    {
        $seed = 42;

        if ($section_ids = $filterParams->getSectionIds() ?: $filterParams->getSectionIdsFilter()) {
            $seed = $section_ids[0];
        } elseif ($producers_ids = $filterParams->getProducers()) {
            $seed = $producers_ids[0];
        }

        $this->setSeed($seed);

        return $this;
    }

    public function setMaxPosition(int $maxPosition): ProductListBanners
    {
        $this->maxPosition = $maxPosition;

        return $this;
    }

    public function setMinPosition(int $minPosition): ProductListBanners
    {
        $this->minPosition = $minPosition;

        return $this;
    }
}
