<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Serenity\CoreBundle\Shared\Twig\Components;

use Doctrine\ORM\EntityManagerInterface;
use Serenity\CoreBundle\Config\Entity\Config;
use Serenity\CoreBundle\Config\Repository\ConfigRepository;
use Serenity\CoreBundle\Notification\Command\NotificationCommand;
use Serenity\CoreBundle\Notification\Enum\PushLevel;
use Serenity\CoreBundle\Shared\Common\ConfigInterface;
use Serenity\CoreBundle\Shared\Form\Type\Config\ConfigType;
use Serenity\MediaBundle\Trait\ComponentMediaTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\TwigComponent\Attribute\PreMount;

#[AsLiveComponent(template: '@components/SerenityCore/ConfigForm.html.twig')]
#[IsGranted('ROLE_ADMIN')]
class ConfigForm extends AbstractController
{
    use ComponentMediaTrait;
    use ComponentToolsTrait;
    use ComponentWithFormTrait;
    use DefaultActionTrait;

    /**
     * @var class-string<FormTypeInterface>
     */
    #[LiveProp]
    public string $formType;

    #[LiveProp]
    public string $messageSuccess;

    public ?Config $config = null;

    public function __construct(private readonly EntityManagerInterface $entityManager,
        private readonly ConfigRepository $configRepository,
        private readonly MessageBusInterface $commandBus,
        private readonly TranslatorInterface $translator,
    ) {
    }

    /**
     * @param array<string> $data
     *
     * @return array<string>
     */
    #[PreMount]
    public function preMount(array $data): array
    {
        $resolver = new OptionsResolver();
        $resolver->setIgnoreUndefined(true);
        $resolver->setDefined('formType');
        $resolver->setDefined('messageSuccess');
        $resolver->setRequired('formType');
        $resolver->setAllowedTypes('formType', ['string']);
        $resolver->setAllowedTypes('messageSuccess', ['string']);

        return $resolver->resolve($data) + $data;
    }

    protected function instantiateForm(): FormInterface
    {
        $resolver = new OptionsResolver();
        /** @var FormTypeInterface $formTypeInstance */
        $formTypeInstance = new $this->formType();
        $formTypeInstance->configureOptions($resolver);
        $class = $resolver->resolve()['data_class']
            ?? throw new \LogicException("Missing 'class' option in form configuration.");

        /** @var ConfigInterface $configInstance */
        $configInstance = new $class();
        $this->config = $this->configRepository->findOne($configInstance->getName());

        return $this->createForm(ConfigType::class, $this->config, ['configType' => $this->formType]);
    }

    #[LiveAction]
    public function save(Request $request): ?Response
    {
        if ($this->uploadedFiles) {
            $this->formValues = array_replace_recursive($this->formValues, $this->deserializeUploadedFiles($this->uploadedFiles));
        }

        try {
            $this->submitForm();
            if ($this->form instanceof FormInterface) {
                /** @var Config $newConfig */
                $newConfig = $this->form->getData();
                /* Comme les configs sont sérialisées en json dans la base, le hook VichUploader doit être lancé à la main car pas de persist d'image dans la table image */
                /** @var ConfigInterface $value */
                $value = $this->manualPersistAttachment($newConfig->getValue(), $this->deserializeUploadedFiles($this->uploadedFiles));
                $newConfig->setValue(clone $value);
                $this->entityManager->persist($newConfig);
                $this->entityManager->flush();

                $notification = new NotificationCommand(
                    message: $this->messageSuccess
                );
                $this->commandBus->dispatch($notification);

                $this->uploadedFiles = [];
                $this->resetForm();
            }
        } catch (UnprocessableEntityHttpException $e) {
            if ($this->form->getErrors(true)->count() > 0) {
                $notification = new NotificationCommand(
                    message: $this->translator->trans('form.error', [], 'serenity_core_messages.admin'),
                    level: PushLevel::ERROR
                );
                $this->commandBus->dispatch($notification);
            }
        }

        return null;
    }
}
