<?php

namespace NoahVet\Reef\Controller\Common;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use NoahVet\Reef\Controller\AbstractController;
use NoahVet\Reef\Mapper\Common\One\Create\AbstractCreateMapperInterface;
use NoahVet\Reef\Mapper\Common\One\Update\AbstractUpdateMapperInterface;
use NoahVet\Reef\Repository\BaseRepositoryInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;

/**
 * @template EntityType of object
 * @template RequestDtoType of object
 * @template ResponseDtoType of object
 */
abstract class AbstractBulkUpdateController extends AbstractController
{
    /**
     * @param AbstractUpdateMapperInterface<EntityType, RequestDtoType> $mapper
     * @param BaseRepositoryInterface<EntityType>                       $resourceRepository
     * @param class-string<RequestDtoType>                              $requestDtoFqcn
     *
     * @return Collection<int, EntityType>
     */
    protected function deserializeRequest(
        AbstractUpdateMapperInterface $mapper,
        BaseRepositoryInterface $resourceRepository,
        string $requestDtoFqcn,
    ): Collection {
        $payloadAsArray = \json_decode($this->request->getContent(), true);
        if (!\is_array($payloadAsArray)) {
            throw new BadRequestException('Invalid JSON payload.');
        }

        if (!\array_key_exists('resources', $payloadAsArray)) {
            throw new BadRequestException('Missing resources payload.');
        }

        $resources = $payloadAsArray['resources'];
        if (!\is_array($resources)) {
            throw new BadRequestException('Invalid resources payload.');
        }
        /** @var Collection<int, EntityType> $entities */
        $entities = new ArrayCollection();

        foreach ($resources as $resourceKeyValue) {
            /** @var EntityType|null $associatedResource */
            $associatedResource = $resourceRepository->findOneBy($resourceKeyValue);
            if (!$associatedResource) {
                throw new BadRequestException('At least one resource is incorrect.');
            }

            $entities->add(
                $mapper->mapRequestDtoToEntity(
                    $mapper->mapDataToRequestDto(
                        $this->request->getContent(),
                        $requestDtoFqcn,
                        $associatedResource,
                    ),
                ),
            );
        }

        return $entities;
    }

    /**
     * @param AbstractCreateMapperInterface<EntityType, RequestDtoType, ResponseDtoType> $mapper
     * @param EntityType                                                                 $entity
     * @param class-string<ResponseDtoType>                                              $responseDtoFqcn
     *
     * @return array<string, mixed>
     */
    protected function serializeResponse(
        AbstractCreateMapperInterface $mapper,
        object $entity,
        string $responseDtoFqcn,
    ): array {
        return $mapper->normalizeResponseDto(
            $mapper->mapEntityToResponseDto(
                $entity,
                $responseDtoFqcn,
            ),
        );
    }
}
