<?php

declare(strict_types=1);

namespace NoahVet\Reef\Paginator;

use Doctrine\ORM\EntityManagerInterface;
use NoahVet\Reef\Paginator\Resolver\LimitResolverInterface;
use NoahVet\Reef\Paginator\Resolver\PageResolverInterface;
use NoahVet\Reef\Repository\BaseRepositoryInterface;
use Symfony\Component\HttpFoundation\Request;

class HttpPaginator implements HttpPaginatorInterface
{
    /**
     * @var array<string, mixed>
     */
    private array $information = [];

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly LimitResolverInterface $limitResolver,
        private readonly PageResolverInterface $pageResolver,
    ) {
    }

    /**
     * @return array<string, string>
     */
    public function getContentRangeHeader(string $unit): array
    {
        return [
            'Content-Range' => \sprintf(
                '%s %s-%s/%s',
                $unit,
                $this->information['range-start'],
                $this->information['range-end'],
                $this->information['size'],
            ),
        ];
    }

    public function getInformation(): array
    {
        return $this->information;
    }

    /**
     * @template EntityType of object
     *
     * @param class-string<EntityType> $entityFqcn
     * @param array<string, mixed>     $context
     *
     * @return array<int, EntityType>
     */
    public function getResult(
        Request $request,
        string $entityFqcn,
        array $context = [],
        bool $isPaginated = true,
    ): array {
        /** @var BaseRepositoryInterface<EntityType> $entityRepository */
        $entityRepository = $this->entityManager->getRepository($entityFqcn);
        $entityRepository->setLocale($request->getLocale());
        $qb = $entityRepository->prepareQuery(
            $context['permissions'],
            $context['filters'],
            $context['sorting'],
            $context['keywords'],
        );

        $count = $entityRepository->countOnId(clone $qb);
        $limit = $this->limitResolver->resolve($request);
        $lastPage = (int) \floor($count / $limit);
        $page = $this->pageResolver->resolve($request, $lastPage);

        $this->information = [
            'current' => $page,
            'next' => $page < $lastPage ? $page + 1 : null,
            'previous' => 0 < $page ? $page - 1 : null,
            'first' => 0,
            'last' => $lastPage,
            'limit' => $limit,
            'size' => $count,
            'range-start' => $page * $limit,
            'range-end' => \min(($page * $limit) + $limit - 1, $count - 1),
        ];

        return $isPaginated
            ? $entityRepository->findPaginated($page, $limit, $qb)
            : $entityRepository->findAllResult($qb);
    }
}
