<?php
namespace App\Controller;
use App\Entity\Card;
use App\Entity\PlanityRewardCode;
use App\Entity\User;
use App\Service\EmailService;
use App\Service\QrcodeService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
class InscriptionController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly QrcodeService $qrcodeService,
private readonly EmailService $emailService,
private readonly TokenStorageInterface $tokenStorage,
private readonly ValidatorInterface $validator,
private readonly LoggerInterface $logger
) {
}
#[Route('/inscription', name: 'inscription')]
public function index(): Response
{
return $this->render('inscription/inscription.html.twig', [
'controller_name' => 'InscriptionController',
]);
}
#[Route(path: '/api/inscription', name: 'api_register', options: ["expose" => true], methods: ["POST"])]
public function apiInscription(Request $request, UserPasswordHasherInterface $passwordEncoder)
{
try {
$submittedToken = $request->request->get('_csrf_token');
if (!$this->isCsrfTokenValid('register', $submittedToken)) {
return new JsonResponse(['message' => 'Token CSRF invalide'], Response::HTTP_FORBIDDEN);
}
$data = $request->request->all();
// Validation améliorée
$constraints = new Assert\Collection(
[
'fields' => [
'_csrf_token' => new Assert\NotBlank(),
'email' => [
new Assert\NotBlank(['message' => 'L\'email est obligatoire']),
new Assert\Email(['message' => 'L\'email n\'est pas valide']),
new Assert\Length(['max' => 180])
],
'password' => [
new Assert\NotBlank(['message' => 'Le mot de passe est obligatoire']),
new Assert\Length([
'min' => 8,
'minMessage' => 'Le mot de passe doit contenir au moins {{ limit }} caractères'
])
],
'confirm_password' => [
new Assert\NotBlank(),
new Assert\EqualTo([
'propertyPath' => 'password',
'message' => 'Les mots de passe ne correspondent pas'
])
],
'prenom' => [
new Assert\NotBlank(['message' => 'Le prénom est obligatoire']),
new Assert\Length(['min' => 2, 'max' => 50])
],
'nom' => [
new Assert\NotBlank(['message' => 'Le nom est obligatoire']),
new Assert\Length(['min' => 2, 'max' => 50])
],
'telephone' => [
new Assert\NotBlank(['message' => 'Le téléphone est obligatoire']),
new Assert\Regex([
'pattern' => '/^0[1-9][0-9]{8}$/',
'message' => 'Le numéro de téléphone doit commencer par 0 et contenir 10 chiffres'
])
],
'birthdate' => [
new Assert\NotBlank(['message' => 'La date de naissance est obligatoire']),
new Assert\Date(['message' => 'Date de naissance invalide'])
],
'promo_code' => new Assert\Optional([
new Assert\Type(['type' => 'string'])
])
],
'allowExtraFields' => true,
'groups' => ['Default']
]
);
$violations = $this->validator->validate($data, $constraints);
if (count($violations) > 0) {
$errors = [];
foreach ($violations as $violation) {
$errors[$violation->getPropertyPath()] = $violation->getMessage();
}
return new JsonResponse([
'message' => 'Erreurs de validation',
'errors' => $errors
], Response::HTTP_BAD_REQUEST);
}
// Vérification email unique
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $data['email']]);
if ($existingUser) {
return new JsonResponse(['message' => "Cette adresse email est déjà utilisée"], Response::HTTP_CONFLICT);
}
// Création de l'utilisateur
$user = new User();
$user->setEmail(strtolower(trim($data['email'])));
$user->setPhone($data['telephone']);
$user->setFirstname(ucfirst(strtolower(trim($data['prenom']))));
$user->setLastname(ucfirst(strtolower(trim($data['nom']))));
$user->setBirthdate(new \DateTime($data['birthdate']));
$user->setSlug(uniqid());
$user->setRoles(['ROLE_USER']);
// Points de base
$points = 20;
// Vérification du code promo s'il existe
if (!empty($data['promo_code'])) {
$rewardCode = $this->entityManager->getRepository(PlanityRewardCode::class)
->findOneBy([
'code' => $data['promo_code'],
'planityEmail' => $user->getEmail(),
'isUsed' => false
]);
if ($rewardCode && $rewardCode->isValid()) {
$points += $rewardCode->getValue();
$rewardCode->setIsUsed(true);
$this->entityManager->persist($rewardCode);
}
}
// Hashage du mot de passe
$encodedPassword = $passwordEncoder->hashPassword($user, $data['password']);
$user->setPassword($encodedPassword);
$this->entityManager->persist($user);
$this->entityManager->flush();
// Création de la carte de fidélité
$QrCode = $this->qrcodeService->generateQrCode($user);
$card = new Card();
$card->setUser($user);
$card->setQrCodeImage($QrCode);
$card->setPoints($points);
$this->entityManager->persist($card);
$this->entityManager->flush();
// Email et connexion
$this->emailService->sendBienvenueEmail($user->getFirstname() . ', Bienvenue chez IMMY BEAUTY !', $user);
$token = new UsernamePasswordToken($user, 'main', $user->getRoles());
$this->tokenStorage->setToken($token);
$request->getSession()->set('_security_main', serialize($token));
return new JsonResponse([
'success' => true,
'message' => 'Inscription réussie ! Vous êtes maintenant connecté.',
'redirect' => $this->generateUrl('app_fidelite')
], Response::HTTP_CREATED);
} catch (\Exception $e) {
$this->logger->error('Erreur inscription : ' . $e->getMessage());
return new JsonResponse([
'success' => false,
'message' => 'Une erreur est survenue lors de l\'inscription. Veuillez réessayer.',
'debug' => $e->getMessage() // À retirer en production
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
#[Route('/verify-promo-code', name: 'verify_promo_code', methods: ['POST'])]
public function verifyCode(Request $request): JsonResponse
{
// Validation CSRF
if (!$this->isCsrfTokenValid('promo_verify', $request->request->get('_token'))) {
return $this->json([
'valid' => false,
'message' => 'Une erreur est survenue'
], 403);
}
$code = trim($request->request->get('code'));
$email = trim(strtolower($request->request->get('email')));
// Validation basique
if (!$code || !$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->json([
'valid' => false,
'message' => 'Veuillez entrer un email valide et un code'
], 400);
}
// Vérification du code
$rewardCode = $this->entityManager->getRepository(PlanityRewardCode::class)
->findOneBy([
'code' => $code,
'planityEmail' => $email,
'isUsed' => false
]);
if (!$rewardCode) {
return $this->json([
'valid' => false,
'message' => 'Code invalide ou expiré'
], 400);
}
if (!$rewardCode->isValid()) {
return $this->json([
'valid' => false,
'message' => 'Code invalide ou expiré'
], 400);
}
return $this->json([
'valid' => true,
'points' => $rewardCode->getValue(),
'message' => sprintf('Code valide ! %d points seront crédités à la création de votre compte', $rewardCode->getValue())
]);
}
}