<?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\PersonBundle\Form\DataMapper;

use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\PersonIdentifier\Exception\UnexpectedTypeException;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierDefinitionRepository;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\FormInterface;

final readonly class PersonIdentifiersDataMapper implements DataMapperInterface
{
    public function __construct(
        private PersonIdentifierManagerInterface $identifierManager,
        private PersonIdentifierDefinitionRepository $identifierDefinitionRepository,
    ) {}

    public function mapDataToForms($viewData, \Traversable $forms): void
    {
        if (!$viewData instanceof Collection) {
            throw new UnexpectedTypeException($viewData, Collection::class);
        }
        /** @var array<string, FormInterface> $formsByKey */
        $formsByKey = iterator_to_array($forms);

        foreach ($this->identifierManager->getWorkers() as $worker) {
            if (!$worker->getDefinition()->isEditableByUsers()) {
                continue;
            }
            $form = $formsByKey['identifier_'.$worker->getDefinition()->getId()];
            $identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $worker->getDefinition()->getId() === $identifier->getId());
            if (null === $identifier) {
                $identifier = new PersonIdentifier($worker->getDefinition());
            }
            $form->setData($identifier->getValue());
        }
    }

    public function mapFormsToData(\Traversable $forms, &$viewData): void
    {
        if (!$viewData instanceof Collection) {
            throw new UnexpectedTypeException($viewData, Collection::class);
        }

        foreach ($forms as $name => $form) {
            $identifierId = (int) substr((string) $name, 11);
            $identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $identifier->getId() === $identifierId);
            $definition = $this->identifierDefinitionRepository->find($identifierId);
            if (null === $identifier) {
                $identifier = new PersonIdentifier($definition);
                $viewData->add($identifier);
            }
            if (!$identifier->getDefinition()->isEditableByUsers()) {
                continue;
            }

            $worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($definition);
            $identifier->setValue($form->getData());
            $identifier->setCanonical($worker->canonicalizeValue($identifier->getValue()));
        }
    }
}
