<?php

declare(strict_types=1);

namespace NoahVet\Reef\Mapper\Common\One\Update;

use NoahVet\Reef\Domain\Tool\ArrayTool;
use NoahVet\Reef\Mapper\Common\One\AbstractOneMapper;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;

/**
 * @template EntityType of object
 * @template RequestDtoType of object
 * @template ResponseDtoType of object
 *
 * @extends AbstractOneMapper<EntityType>
 *
 * @implements AbstractUpdateMapperInterface<EntityType, RequestDtoType>
 */
abstract class AbstractUpdateMapper extends AbstractOneMapper implements AbstractUpdateMapperInterface
{
    /**
     * @var array<string, mixed>
     */
    protected array $payloadAsArray;

    /**
     * @param class-string<RequestDtoType> $requestDto
     * @param EntityType                   $existingEntity
     *
     * @return RequestDtoType
     */
    public function mapDataToRequestDto(
        string $data,
        string $requestDto,
        object $existingEntity,
    ): object {
        $this->entity = $existingEntity;
        $this->payloadAsArray = $this->serializer->decode(
            $data,
            'json',
        );

        /** @var array<string, mixed> $dataAsArray */
        $dataAsArray = \array_merge(
            $this->normalizeExistingEntity($existingEntity),
            $this->payloadAsArray,
        );

        ArrayTool::applyTrimRecursive($dataAsArray);

        return $this->serializer->denormalize(
            $dataAsArray,
            $requestDto,
            'json',
            [
                AbstractNormalizer::IGNORED_ATTRIBUTES => [
                    'id',
                    'createdDate',
                    'updatedDate',
                ],
            ],
        );
    }

    /**
     * @param EntityType $entity
     *
     * @return array<string, mixed>
     *
     * @throws ExceptionInterface
     */
    public function normalizeExistingEntity(
        object $entity,
    ): array {
        $data = $this->normalizeEntity($entity);
        $this->postNormalizeEntity($data);

        return $data;
    }
}
