<?php

namespace NoahVet\Reef\Controller\Common;

use NoahVet\Reef\Controller\AbstractController;
use NoahVet\Reef\Factory\RequestContext\RequestContextFactoryInterface;
use NoahVet\Reef\Mapper\Common\Many\AbstractGetAllMapperInterface;
use NoahVet\Reef\Paginator\HttpPaginatorInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

/**
 * @template EntityType of object
 * @template ResponseDtoType of object
 */
abstract class AbstractListController extends AbstractController
{
    public function __construct(
        protected readonly HttpPaginatorInterface $httpPaginator,
        protected readonly RequestContextFactoryInterface $requestContextFactory,
    ) {
    }

    #[\Override]
    public function preInvoke(): void
    {
        $this->mapQueryParamToWhitelist();
    }

    /**
     * @param array<int, mixed>|\ArrayObject<(int|string), mixed>|bool|float|int|string|null $data
     */
    protected function getResponse(array|\ArrayObject|bool|float|int|string|null $data): Response
    {
        if (0 < $this->httpPaginator->getInformation()['last']) {
            $this->headers = \array_merge($this->headers, $this->httpPaginator->getContentRangeHeader('resources'));
        }

        return new JsonResponse(
            $data,
            0 < $this->httpPaginator->getInformation()['last'] ? Response::HTTP_PARTIAL_CONTENT : Response::HTTP_OK,
            $this->headers,
        );
    }

    /**
     * @template T of object
     *
     * @param class-string<T> $entityFqcn
     *
     * @return array<int, T>
     */
    protected function getPaginatedEntities(string $entityFqcn): array
    {
        return $this->httpPaginator->getResult(
            $this->request,
            $entityFqcn,
            $this->requestContextFactory->createContextFromRequest(
                $this->request,
                $this->whitelist,
            ),
        );
    }

    /**
     * @param AbstractGetAllMapperInterface<EntityType, ResponseDtoType> $mapper
     * @param class-string<EntityType>                                   $entityFqcn
     * @param class-string<ResponseDtoType>                              $responseDtoFqcn
     *
     * @return array<int, mixed>|\ArrayObject<(int|string), mixed>|bool|float|int|string|null
     */
    protected function serializeResponse(
        AbstractGetAllMapperInterface $mapper,
        string $entityFqcn,
        string $responseDtoFqcn,
    ): array|\ArrayObject|bool|float|int|string|null {
        return $mapper->normalizeResponseDto(
            $mapper->mapDataToResponseDto(
                $this->getPaginatedEntities($entityFqcn),
                $responseDtoFqcn,
            ),
        );
    }
}
