<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Tests\Pagination;

use Chill\MainBundle\Pagination\Paginator;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use function count;

/**
 * Test the paginator class.
 *
 * @internal
 *
 * @coversNothing
 */
final class PaginatorTest extends KernelTestCase
{
    protected $paginator;

    protected $prophet;

    protected function setUp(): void
    {
        $this->prophet = new \Prophecy\Prophet();
    }

    public function testGetPage()
    {
        $paginator = $this->generatePaginator(105, 10);

        $this->assertEquals(5, $paginator->getPage(5)->getNumber());
    }

    /**
     * @param int  $totalItems
     * @param int  $itemPerPage
     * @param int  $currentPage
     * @param bool $expectedHasNextPage
     *
     * @dataProvider generateHasNextPage
     */
    public function testHasNextPage(
        $totalItems,
        $itemPerPage,
        $currentPage,
        $expectedHasNextPage,
    ) {
        $paginator = $this->generatePaginator($totalItems, $itemPerPage, $currentPage);

        $this->assertEquals($expectedHasNextPage, $paginator->hasNextPage());
    }

    /**
     * generate an array with a set of page with :
     * - total items ;
     * - item per page ;
     * - current page number ;
     * - expected hasNextPage result.
     *
     * @return array
     */
    public static function generateHasNextPage()
    {
        return [
            [10, 10, 1, false],
            [20, 10, 1, true],
            [12, 10, 1, true],
            [12, 10, 2, false],
        ];
    }

    /**
     * test the HasPage function.
     *
     * @param int  $totalItems
     * @param int  $itemPerpage
     * @param int  $pageNumber
     * @param bool $expectedHasPage
     *
     * @dataProvider generateHasPage
     */
    public function testHasPage(
        $totalItems,
        $itemPerpage,
        $pageNumber,
        $expectedHasPage,
    ) {
        $paginator = $this->generatePaginator($totalItems, $itemPerpage);

        $this->assertEquals($expectedHasPage, $paginator->hasPage($pageNumber));
    }

    public static function generateHasPage()
    {
        return [
            [10, 10, -1, false],
            [12, 10, 1,  true],
            [12, 10, 2,  true],
            [12, 10, 3,  false],
        ];
    }

    /**
     * @param int $totalItems
     * @param int $itemPerPage
     * @param int $currentPage
     *
     * @dataProvider generateHasPreviousPage
     */
    public function testHasPreviousPage(
        $totalItems,
        $itemPerPage,
        $currentPage,
        mixed $expectedHasNextPage,
    ) {
        $paginator = $this->generatePaginator($totalItems, $itemPerPage, $currentPage);

        $this->assertEquals($expectedHasNextPage, $paginator->hasPreviousPage());
    }

    /**
     * generate an array with a set of page with :
     * - total items ;
     * - item per page ;
     * - current page number ;
     * - expected hasPreviousPage result.
     *
     * @return array
     */
    public static function generateHasPreviousPage()
    {
        return [
            [10, 10, 1, false],
            [20, 10, 1, false],
            [12, 10, 1, false],
            [12, 10, 2, true],
        ];
    }

    /**
     * Test that the countPages method (and his alias `count`) return
     * valid results.
     *
     * @param int $totalItems
     * @param int $itemPerPage
     * @param int $expectedPageNumber
     *
     * @dataProvider generatePageNumber
     */
    public function testPageNumber($totalItems, $itemPerPage, $expectedPageNumber)
    {
        $paginator = $this->generatePaginator($totalItems, $itemPerPage);

        $this->assertEquals($expectedPageNumber, $paginator->countPages());
        $this->assertEquals($expectedPageNumber, \count($paginator));
    }

    /**
     * generate a set of pages with different maxItem, itemPerPage, and
     * expected page number.
     *
     * @return array
     */
    public static function generatePageNumber()
    {
        return [
            [12, 10, 2],
            [20, 10, 2],
            [21, 10, 3],
            [19, 10, 2],
            [1, 10, 1],
            [0, 10, 1],
            [10, 10, 1],
        ];
    }

    public function testPagesGenerator()
    {
        $paginator = $this->generatePaginator(105, 10);

        $generator = $paginator->getPagesGenerator();

        $i = 1;

        foreach ($generator as $page) {
            $this->assertEquals(
                $i,
                $page->getNumber(),
                "assert that the current page number is {$i}"
            );
            ++$i;
        }

        $this->assertEquals(
            11,
            $page->getNumber(),
            'assert that the last page number is 11'
        );
    }

    public function testPagesWithoutResult()
    {
        $paginator = $this->generatePaginator(0, 10);

        $this->assertEquals(0, $paginator->getCurrentPageFirstItemNumber());
        $this->assertEquals(10, $paginator->getItemsPerPage());
    }

    /**
     * @param int    $itemPerPage
     * @param string $route
     *
     * @return Paginator
     */
    protected function generatePaginator(
        mixed $totalItems,
        $itemPerPage,
        mixed $currentPageNumber = 1,
        $route = '',
        array $routeParameters = [],
    ) {
        $urlGenerator = $this->prophet->prophesize();
        $urlGenerator->willImplement(UrlGeneratorInterface::class);

        return new Paginator(
            $totalItems,
            $itemPerPage,
            $currentPageNumber,
            $route,
            $routeParameters,
            $urlGenerator->reveal(),
            'page',
            'item_per_page'
        );
    }
}
