<?php

/*
 * This file is part of the VITICORE2 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 CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Security;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Security\Model\RegisterConfirmRequest;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Security\RegisterConfirmType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Command\NotificationCommand;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushLevel;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Security\AccountActivationService;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Behavior\HasTemplateOverride;
use CpCreation\VitiCore\User\ImportedClientGroupAssigner;
use CpCreation\VitiCore\User\Model\User;
use CpCreation\VitiCore\User\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
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\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('security:RegisterConfirmForm', template: '@components/security/RegisterConfirmForm.html.twig')]
final class RegisterConfirmForm extends AbstractController
{
    use ComponentWithFormTrait;
    use DefaultActionTrait;
    use HasTemplateOverride;

    private const SESSION_REGISTER_EMAIL = 'security.register.email';
    private const SESSION_REGISTER_MARKETING = 'security.register.marketing_opt_in';
    private const SESSION_ACTIVATION_PENDING_EMAIL = 'security.activation_pending.email';

    #[LiveProp(writable: true)]
    public ?string $email = null;

    #[LiveProp(writable: true)]
    public bool $marketingOptIn = true;

    public function __construct(
        private readonly UserRepository $userRepository,
        private readonly RequestStack $requestStack,
        private readonly MessageBusInterface $commandSimpleBus,
        private readonly AccountActivationService $accountActivationService,
        private readonly TranslatorInterface $translator,
        private readonly ImportedClientGroupAssigner $importedClientGroupAssigner,
    ) {}

    protected function instantiateForm(): FormInterface
    {
        $session = $this->getSession();
        $this->email ??= $session?->get(self::SESSION_REGISTER_EMAIL);
        $this->marketingOptIn = $this->marketingOptIn || (bool) $session?->get(self::SESSION_REGISTER_MARKETING, false);

        $data = new RegisterConfirmRequest();
        $data->setEmail($this->email);

        return $this->createForm(RegisterConfirmType::class, $data);
    }

    #[LiveAction]
    public function validatePasswords(): void
    {
        $formName = $this->getFormName();
        $this->validatedFields = [
            $formName,
            $formName . '.plainPassword',
            $formName . '.passwordConfirmation',
        ];

        try {
            $this->submitForm(false);
        } catch (UnprocessableEntityHttpException) {
        }
    }

    #[LiveAction]
    public function submitRegisterConfirm(): ?Response
    {
        try {
            $this->submitForm();
        } catch (UnprocessableEntityHttpException) {
            $this->notifyFirstFormError();

            return null;
        }

        $data = $this->form?->getData();
        if (!$data instanceof RegisterConfirmRequest) {
            return null;
        }

        $email = $this->normalizeEmail($data->getEmail());

        if (null !== $this->userRepository->findVerifiedByEmail($email)) {
            $this->clearPendingRegistration();
            $this->notify($this->translator->trans('security.error.account_exists', [], 'messages'), PushLevel::ERROR);

            return $this->redirectToRoute('app_login');
        }

        $user = $this->userRepository->findLatestUnverifiedByEmail($email) ?? new User();
        $this->userRepository->deleteOtherUnverifiedAccountsByEmail($email, $user);
        $user->setEmail($email);
        $user->setPlainPassword($data->getPlainPassword());
        $user->setRoles(['ROLE_USER']);
        $user->setMarketingOptIn($this->marketingOptIn);
        $user->setIsVerified(false);
        $user->setResetPasswordToken(null);
        $user->setResetPasswordRequestedAt(null);
        $user->setResetPasswordExpiresAt(null);
        $user->setLastLinkRequestedAt(null);
        $this->importedClientGroupAssigner->assignTo($user);

        $this->userRepository->save($user);
        $this->accountActivationService->sendActivationEmail($user, true);
        $this->clearPendingRegistration();
        $this->getSession()?->set(self::SESSION_ACTIVATION_PENDING_EMAIL, $user->getEmail());

        return $this->redirectToRoute('app_register_pending');
    }

    public function canSubmit(): bool
    {
        $values = $this->formValues;
        $email = $values['email'] ?? null;
        $plainPassword = $values['plainPassword'] ?? null;
        $passwordConfirmation = $values['passwordConfirmation'] ?? null;

        return \is_string($email)
            && false !== filter_var($email, \FILTER_VALIDATE_EMAIL)
            && \is_string($plainPassword)
            && '' !== mb_trim($plainPassword)
            && \is_string($passwordConfirmation)
            && '' !== mb_trim($passwordConfirmation)
            && $plainPassword === $passwordConfirmation;
    }

    private function getSession(): ?SessionInterface
    {
        return $this->requestStack->getCurrentRequest()?->getSession();
    }

    private function clearPendingRegistration(): void
    {
        $session = $this->getSession();
        $session?->remove(self::SESSION_REGISTER_EMAIL);
        $session?->remove(self::SESSION_REGISTER_MARKETING);
    }

    private function notifyFirstFormError(): void
    {
        $error = $this->form?->getErrors(true)->current();
        if (null === $error) {
            return;
        }

        $this->notify($error->getMessage(), PushLevel::ERROR);
    }

    private function notify(string $message, PushLevel $level): void
    {
        $this->commandSimpleBus->dispatch(new NotificationCommand(
            message: $message,
            level: $level,
        ));
    }

    private function normalizeEmail(?string $email): string
    {
        return mb_strtolower(mb_trim((string) $email));
    }
}
