<?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\Service\DocGenerator;

use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * @implements DocGeneratorContextWithPublicFormInterface<AccompanyingPeriodWorkEvaluation>
 */
class AccompanyingPeriodWorkEvaluationContext implements
    DocGeneratorContextWithAdminFormInterface,
    DocGeneratorContextWithPublicFormInterface
{
    public function __construct(private readonly AccompanyingPeriodWorkContext $accompanyingPeriodWorkContext, private readonly EntityManagerInterface $em, private readonly EvaluationRepository $evaluationRepository, private readonly NormalizerInterface $normalizer, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly ThirdPartyRender $thirdPartyRender, private readonly TranslatorInterface $translator) {}

    public function adminFormReverseTransform(array $data): array
    {
        return array_merge(
            $this->accompanyingPeriodWorkContext->adminFormReverseTransform($data),
            [
                'evaluations' => array_map(
                    static fn (Evaluation $e) => $e->getId(),
                    $data['evaluations']
                ),
            ]
        );
    }

    public function adminFormTransform(array $data): array
    {
        return array_merge(
            $this->accompanyingPeriodWorkContext->adminFormTransform($data),
            [
                'evaluations' => array_map(
                    fn ($id) => $this->evaluationRepository->find($id),
                    $data['evaluations'] ?? []
                ),
            ]
        );
    }

    public function buildAdminForm(FormBuilderInterface $builder): void
    {
        $this->accompanyingPeriodWorkContext->buildAdminForm($builder);

        $builder->remove('category');

        $builder->add('evaluations', EntityType::class, [
            'class' => Evaluation::class,
            'label' => 'Linked evaluations',
            'choices' => $this->evaluationRepository->findAll(),
            'choice_label' => fn (Evaluation $e) => $this->translatableStringHelper->localize($e->getTitle()),
            'multiple' => true,
            'attr' => ['class' => 'select2'],
        ]);
    }

    public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
    {
        $this->accompanyingPeriodWorkContext->buildPublicForm($builder, $template, $entity->getAccompanyingPeriodWork());

        $thirdParties = [...array_values(array_filter($entity->getAccompanyingPeriodWork()->getThirdParties()->toArray())), ...array_values(array_filter([$entity->getAccompanyingPeriodWork()->getHandlingThierParty()])), ...array_values(
            array_filter(
                array_map(
                    fn (Resource $r): ?ThirdParty => $r->getThirdParty(),
                    $entity->getAccompanyingPeriodWork()->getAccompanyingPeriod()->getResources()->filter(
                        static fn (Resource $r): bool => null !== $r->getThirdParty()
                    )->toArray()
                )
            )
        )];

        $options = $template->getOptions();
        if ($options['thirdParty'] ?? false) {
            $builder->add('thirdParty', EntityType::class, [
                'class' => ThirdParty::class,
                'choices' => $thirdParties,
                'choice_label' => fn (ThirdParty $p) => $this->thirdPartyRender->renderString($p, []),
                'multiple' => false,
                'required' => false,
                'expanded' => true,
                'label' => $options['thirdPartyLabel'],
                'placeholder' => $this->translator->trans('Any third party selected'),
            ]);
        }
    }

    public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
    {
        $data = $this->accompanyingPeriodWorkContext
            ->getData($template, $entity->getAccompanyingPeriodWork(), $contextGenerationData);
        $data['evaluation'] = $this->normalizer->normalize(
            $entity,
            'docgen',
            [
                'docgen:expect' => AccompanyingPeriodWorkEvaluation::class,
                AbstractNormalizer::GROUPS => ['docgen:read'],
            ]
        );

        return $data;
    }

    public function getDescription(): string
    {
        return 'docgen.A context for accompanying period work evaluation';
    }

    public function getEntityClass(): string
    {
        return AccompanyingPeriodWorkEvaluation::class;
    }

    public function getFormData(DocGeneratorTemplate $template, $entity): array
    {
        return $this->accompanyingPeriodWorkContext->getFormData(
            $template,
            $entity->getAccompanyingPeriodWork()
        );
    }

    public static function getKey(): string
    {
        return 'accompanying_period_work_evaluation_regular';
    }

    public function getName(): string
    {
        return 'docgen.Accompanying period work context';
    }

    public function hasAdminForm(): bool
    {
        return true;
    }

    /**
     * @param AccompanyingPeriodWorkEvaluation $entity
     */
    public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
    {
        return $this->accompanyingPeriodWorkContext
            ->hasPublicForm($template, $entity->getAccompanyingPeriodWork());
    }

    public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
    {
        return $this->accompanyingPeriodWorkContext
            ->contextGenerationDataNormalize($template, $entity->getAccompanyingPeriodWork(), $data);
    }

    public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
    {
        return $this->accompanyingPeriodWorkContext
            ->contextGenerationDataDenormalize($template, $entity->getAccompanyingPeriodWork(), $data);
    }

    public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
    {
        $doc = new AccompanyingPeriodWorkEvaluationDocument();
        $doc->setStoredObject($storedObject)
            ->setAccompanyingPeriodWorkEvaluation($entity)
            ->setTemplate($template)
            ->setTitle($this->translatableStringHelper->localize($template->getName()));
        $this->em->persist($doc);
    }
}
