<?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\JobBundle\Form\ChoiceLoader;

use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Chill\JobBundle\Entity\Rome\Appellation;
use Doctrine\ORM\EntityManagerInterface;
use Chill\JobBundle\Entity\Rome\Metier;
use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
 * Lazy load third parties.
 */
class RomeAppellationChoiceLoader implements ChoiceLoaderInterface
{
    /**
     * @var Appellation[]
     */
    protected $lazyLoadedAppellations = [];

    /**
     * @var EntityManagerInterface
     */
    protected $em;

    /**
     * @var \Chill\JobBundle\Repository\Rome\AppellationRepository
     */
    protected $appellationRepository;

    /**
     * @var PartenaireRomeAppellation;
     */
    protected $apiAppellation;

    /**
     * @var ValidatorInterface
     */
    protected $validator;

    /**
     * @var array<string, array{0: Appellation, 1: Metier}>
     */
    private $toBeCreated = [];

    public function __construct(
        EntityManagerInterface $em,
        PartenaireRomeAppellation $apiAppellation,
        ValidatorInterface $validator,
    ) {
        $this->em = $em;
        $this->apiAppellation = $apiAppellation;
        $this->appellationRepository = $em->getRepository(Appellation::class);
        $this->validator = $validator;
    }

    public function loadChoiceList($value = null): ChoiceListInterface
    {
        return new ArrayChoiceList($this->lazyLoadedAppellations, $value);
    }

    public function loadChoicesForValues($values, $value = null)
    {
        $choices = [];
        $code = '';

        foreach ($values as $v) {
            if (null === $v || '' === $v) {
                continue;
            }

            // start with "original-" ? then we load from api
            if (str_starts_with($v, 'original-')) {
                $code = \substr($v, \strlen('original-'));
                $appellation = $this->appellationRepository->findOneBy(['code' => $code]);

                if ($appellation) {
                    $metier = $appellation->getMetier();
                }

                if (null === $appellation) {
                    if (array_key_exists($v, $this->toBeCreated)) {
                        [$appellation, $metier] = $this->toBeCreated[$v];
                    }
                }
            } else {
                $id = $v;
                $appellation = $this->appellationRepository->find($id);
                $metier = $appellation->getMetier();
            }

            if (null === $appellation && '' !== $code) {
                $def = $this->apiAppellation->getAppellation($code);

                $metier ??= $this->em->getRepository(Metier::class)
                    ->findOneBy(['code' => $def['metier']['code']])
                    ?? (new Metier())
                        ->setCode($def['metier']['code'])
                        ->setLibelle($def['metier']['libelle']);

                $appellation = new Appellation();

                $appellation
                    ->setCode($def['code'])
                    ->setLibelle($def['libelle'])
                    ->setMetier($metier)
                ;

                $errorsAppellation = $this->validator->validate($appellation);
                $errorsMetier = $this->validator->validate($metier);

                if (0 === $errorsAppellation->count() && 0 === $errorsMetier->count()) {
                    $this->toBeCreated[$v] = [$appellation, $metier];
                    $this->em->persist($appellation);
                }
            }

            if ($this->em->contains($metier) and $this->em->contains($appellation)) {
                $choices[] = $appellation;
            }
        }

        return $choices;
    }

    public function loadValuesForChoices(array $choices, $value = null)
    {
        $values = [];

        foreach ($choices as $choice) {
            if (null === $choice) {
                $values[] = null;
                continue;
            }

            $id = \call_user_func($value, $choice);

            $this->lazyLoadedAppellations[$id] = $choice;
        }

        return $this->loadChoiceList($value)->getValuesForChoices($choices);
    }
}
