<?php

namespace NoahVet\Reef\Application\Business;

use NoahVet\Reef\Domain\Business\Model\AbstractEntityWithTeam;
use NoahVet\Reef\Domain\Business\Model\TeamMember;

/**
 * Factory template-method pour construire des modèles WithTeam depuis un lot d'entités.
 *
 * Les sous-classes implémentent {@see build()} pour mapper une entité + ses réponses utilisateur
 * vers leur sous-classe concrète de {@see AbstractEntityWithTeam}, et {@see doGetTeamMembers()}
 * pour déléguer à leur énumérateur de membres d'équipe spécifique.
 */
abstract class AbstractWithTeamFactory
{
    /**
     * @param \Closure(object): TeamMember $teamMemberMapper  Convertit une réponse utilisateur en TeamMember
     * @param \Closure(object): string     $entityIdExtractor Extrait l'UUID depuis l'entité de base
     */
    public function __construct(
        protected readonly \Closure $teamMemberMapper,
        protected readonly \Closure $entityIdExtractor,
    ) {
    }

    /**
     * @param array<int, object> $entities
     *
     * @return list<AbstractEntityWithTeam>
     */
    public function createBatch(array $entities): array
    {
        $teamMembers = $this->getTeamMembers($entities);

        return \array_values(\array_map(
            fn (object $entity): AbstractEntityWithTeam => $this->build(
                $entity,
                $teamMembers[($this->entityIdExtractor)($entity)] ?? [],
            ),
            $entities,
        ));
    }

    public function create(mixed $entity): AbstractEntityWithTeam
    {
        $teamMembers = $this->getTeamMembers([$entity]);

        return $this->build($entity, $teamMembers[$entity->getId()] ?? []);
    }

    /**
     * Construit un modèle WithTeam depuis une entité et ses réponses membres d'équipe résolues.
     *
     * @param array<mixed> $userResponses
     */
    abstract protected function build(mixed $entity, array $userResponses): AbstractEntityWithTeam;

    /**
     * Récupère les membres d'équipe pour un lot d'entités, groupés par ID d'entité.
     *
     * @param array<int, object> $entities
     *
     * @return array<string, list<object>>
     */
    abstract protected function getTeamMembers(array $entities): array;
}
