<?php

namespace KupShop\CatalogBundle\Tests;

use KupShop\CatalogBundle\ProductList\ProductList;
use KupShop\CatalogBundle\ProductList\ProductListBanners;

class ProductListBannersTest extends \DatabaseTestCase
{
    protected ?ProductListBanners $banners = null;
    protected ?ProductList $productList = null;

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

        $this->productList = $this->get(ProductList::class);
        $this->banners = $this->get(ProductListBanners::class);
        $this->banners->setSeed(10)
            ->setSliderId(1)
            ->setStepSize(5);
    }

    /** @dataProvider data_testProductListPager */
    public function testProductListPager(int $page, bool $withBanners, int $expectedTotalPages, int $expectedOnPreviousPages, int $expectedOnThisPage): void
    {
        $productList = $this->productList;
        $pager = new \Pager();
        $pager->setOnPage(11)->setPageNumber($page);

        if ($withBanners) {
            $this->banners->attach($productList, $pager);
        }

        $products = $productList->andSpec($pager->getSpec())
            ->getProducts($totalCount);

        $pager->setTotal($totalCount);

        [$onThisPage, $onPreviousPages] = $pager->getExtraItems();

        $totalBannersOnPage = 0;
        foreach ($products as $product) {
            if (!empty($product->banner)) {
                $totalBannersOnPage++;
            }
        }

        // pokud je pocet produkty mensi jak stepSize na ProductListBanners, tak se na aktualni stranku da vzdy jen jeden banner
        // i kdyz v pageru predpokladam, ze se jich tam da vic, ale jelikoz neznam pocet polozek, tak dokazu jen predpokladat
        if (5 > $products->count()) {
            $onThisPage = 1;
        }

        $this->assertEquals($totalBannersOnPage, $onThisPage, 'Assert count of banners in products list vs expected count of banners (extra items) from pager');
        $this->assertEquals($expectedTotalPages, $pager->count, 'Expected total pages');
        $this->assertEquals($expectedOnThisPage, $onThisPage, 'Expected banners on active page');
        $this->assertEquals($expectedOnPreviousPages, $onPreviousPages, 'Expected banners on previous pages');
    }

    public function data_testProductListPager(): iterable
    {
        yield 'Expecting 1 page in total because no banner was added' => [1, false, 1, 0, 0, 0];
        yield 'Expecting 2 pages in total and 2 banners on first page' => [1, true, 2, 0, 2];
        yield 'Expecting 2 pages in total, page 2 is active so 2 banners on previous pages and 1 banners at active page' => [2, true, 2, 2, 1];
    }

    /**
     * @dataProvider data_testBannersCount
     */
    public function testBannersCount($from, $count)
    {
        $this->assertEquals($count, $this->banners->getSkippedProductsCount($from));
    }

    public function data_testBannersCount()
    {
        return [
            [0, 0],
            [5, 1],
            [10, 2],
            [12, 2], // Jeste je za
            [15, 3], // Uz je ve vysledku
        ];
    }

    public function testProducts()
    {
        $productList = $this->productList;
        $pager = new \Pager();
        $pager->setOnPage(10)->setPageNumber(1);

        $this->banners->attach($productList, $pager);

        $products = $productList->getProducts()->getValues();

        $this->assertObjectNotHasAttribute('banner', $products[0]);
        $this->assertObjectHasAttribute('banner', $products[1]);
        $this->assertObjectNotHasAttribute('banner', $products[3]);
    }

    /**
     * @dataProvider data_testProductsContinuity
     */
    public function testProductsContinuity($pageSize, $stepSize, $count)
    {
        $this->banners->setStepSize($stepSize);
        if ($count !== null) {
            $this->banners->setCount($count);
        }

        $productList = $this->productList;
        $productsCount = $productList->getProductsCount();
        // pager's total number of records = pocet banneru + pocet produktu
        $pagersTotal = $count + $productsCount;

        $visitedIDs = [];
        $pageItemsCount = $pageSize;

        foreach (range(0, $productsCount) as $pageNumber) {
            $pager = new \Pager();
            $pager->setOnPage($pageSize);
            $pager->setPageNumber($pageNumber + 1);

            $productList = clone $this->productList;
            $this->banners->attach($productList, $pager);
            // pager's total = pocet banneru
            $pager->total = $this->banners->getCount();

            $totalProducts = 0;
            $products = $productList->andSpec($pager->getSpec())
                ->getProducts($totalProducts);
            // pager's total += pocet produktu
            $pager->setTotal($totalProducts);
            // pager's total number of records = pocet banneru + pocet produktu
            $this->assertEquals($pager->total, $pagersTotal);

            // End of list
            if (!$products->count()) {
                break;
            }

            // Assert items count from last page - this avoids error on last page
            $this->assertEquals($pageSize, $pageItemsCount);

            $pageItemsCount = 0;
            foreach ($products as $id => $product) {
                $this->assertArrayNotHasKey($id, $visitedIDs);
                $visitedIDs[$id] = true;

                $pageItemsCount++;

                if (isset($product->banner)) {
                    $pageItemsCount++;
                }
            }
        }

        $this->assertCount($productsCount, $visitedIDs);
    }

    /**
     * Pokud byl na strance banner se skrytym sliderem, tak se na druhe strance zobrazoval jako prvni produkt ten, ktery byl posledni
     * na prvni strance.
     */
    public function testProductsWithHiddenSlider(): void
    {
        $this->banners->setSliderId(2)
            ->setStepSize(10);

        $pager = new \Pager();
        $pager->setOnPage(6)
            ->setPageNumber(1);

        $productList = clone $this->productList;
        $this->banners->attach($productList, $pager);

        $productsPage1 = $productList->getProducts()->getKeys();

        $this->assertCount(6, $productsPage1, 'Overim, ze je na strance spravnej pocet produktu');
        $this->assertEquals(6, end($productsPage1), 'Overim, ze stranka konci produktem s ID 6');

        $pager->setPageNumber(2);

        $productList = clone $this->productList;
        $this->banners->attach($productList, $pager);

        $productsPage2 = $productList->getProducts()->getKeys();

        $this->assertCount(5, $productsPage2, 'Overim, ze je na strance spravnej pocet produktu');
        $this->assertEquals(7, reset($productsPage2), 'Overim, ze stranka zacina produktem s ID 7');
    }

    public function data_testProductsContinuity()
    {
        return [
            [4, 2, 5],
            [10, 5, null],
            [10, 3, null],
            [6, 6, 1],
            [6, 5, null],
        ];
    }

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