NewsletterController.java

package com.archiweb.api;

import com.archiweb.model.NewsletterEmail;
import com.archiweb.model.NewsletterSubscriber;
import com.archiweb.model.User;
import com.archiweb.repository.NewsletterEmailRepository;
import com.archiweb.repository.NewsletterSubscriberRepository;
import com.archiweb.repository.UserRepository;
import com.archiweb.service.EmailService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/newsletter")
@RequiredArgsConstructor
@Tag(
        name = "Newsletter",
        description = """
        Gestion de l'abonnement à la newsletter et envoi d'emails groupés.
        """
)
public class NewsletterController {

    private final NewsletterSubscriberRepository newsletterSubscriberRepository;
    private final NewsletterEmailRepository newsletterEmailRepository;
    private final UserRepository userRepository;
    private final EmailService emailService;

    @Operation(
            summary = "S'abonner à la newsletter",
            description = """
            Permet à un utilisateur de s'abonner à la newsletter en fournissant son adresse email.
            """,
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "Adresse email de l'utilisateur",
                    required = true,
                    content = @Content(
                            schema = @Schema(implementation = NewsletterSubscribeRequest.class),
                            examples = @ExampleObject(
                                    value = """
                                    {
                                      "email": "user@example.com"
                                    }
                                    """
                            )
                    )
            ),
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Abonnement réussi"
                    ),
                    @ApiResponse(
                            responseCode = "400",
                            description = "Email invalide ou manquant"
                    )
            }
    )
    @PostMapping("/subscribe")
    public ResponseEntity<?> subscribe(@RequestBody NewsletterSubscribeRequest request) {
        if (request.getEmail() == null || request.getEmail().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "L'adresse email est requise"));
        }

        String email = request.getEmail().trim().toLowerCase();

        // Vérifier si l'utilisateur existe
        java.util.Optional<User> userOptional = userRepository.findByEmail(email);

        if (userOptional.isPresent()) {
            // Si l'utilisateur existe, mettre à jour son consentement
            User user = userOptional.get();
            user.setConsentGiven(true);
            userRepository.save(user);
            
            // Enregistrer aussi dans la table newsletter_subscribers si pas déjà présent
            if (!newsletterSubscriberRepository.existsByEmail(email)) {
                NewsletterSubscriber subscriber = NewsletterSubscriber.builder()
                        .email(email)
                        .consentGiven(true)
                        .active(true)
                        .build();
                newsletterSubscriberRepository.save(subscriber);
            } else {
                // Mettre à jour le consentement dans newsletter_subscribers
                java.util.Optional<NewsletterSubscriber> subscriberOpt = newsletterSubscriberRepository.findByEmail(email);
                if (subscriberOpt.isPresent()) {
                    NewsletterSubscriber subscriber = subscriberOpt.get();
                    subscriber.setConsentGiven(true);
                    subscriber.setActive(true);
                    newsletterSubscriberRepository.save(subscriber);
                }
            }
            
            return ResponseEntity.ok(Map.of("message", "Vous êtes maintenant abonné à notre newsletter !"));
        } else {
            // Si l'utilisateur n'existe pas, enregistrer dans newsletter_subscribers
            java.util.Optional<NewsletterSubscriber> existingSubscriber = newsletterSubscriberRepository.findByEmail(email);
            
            if (existingSubscriber.isPresent()) {
                // Mettre à jour l'abonné existant
                NewsletterSubscriber subscriber = existingSubscriber.get();
                subscriber.setConsentGiven(true);
                subscriber.setActive(true);
                newsletterSubscriberRepository.save(subscriber);
            } else {
                // Créer un nouvel abonné
                NewsletterSubscriber subscriber = NewsletterSubscriber.builder()
                        .email(email)
                        .consentGiven(true)
                        .active(true)
                        .build();
                newsletterSubscriberRepository.save(subscriber);
            }
            
            return ResponseEntity.ok(Map.of("message", "Vous êtes maintenant abonné à notre newsletter !"));
        }
    }

    @Operation(
            summary = "Compter les abonnés actifs",
            description = """
            Retourne le nombre total d'abonnés actifs à la newsletter.
            Accessible uniquement aux administrateurs et employés.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Nombre d'abonnés récupéré avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @GetMapping("/subscribers/count")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<Map<String, Long>> getSubscriberCount() {
        long count = newsletterSubscriberRepository.count();
        return ResponseEntity.ok(Map.of("count", count));
    }

    @Operation(
            summary = "Envoyer un email à tous les abonnés",
            description = """
            Envoie un email newsletter à tous les abonnés actifs.
            L'email est enregistré dans l'historique.
            Accessible uniquement aux administrateurs et employés.
            """,
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "Contenu de l'email",
                    required = true,
                    content = @Content(
                            schema = @Schema(implementation = SendNewsletterRequest.class),
                            examples = @ExampleObject(
                                    value = """
                                    {
                                      "subject": "Nouvelle offre spéciale !",
                                      "content": "Découvrez nos nouveaux thés..."
                                    }
                                    """
                            )
                    )
            ),
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Email envoyé avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "400",
                            description = "Données invalides"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @PostMapping("/send")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> sendNewsletter(
            @RequestBody SendNewsletterRequest request,
            Authentication authentication) {
        
        // Validation
        if (request.getSubject() == null || request.getSubject().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le sujet est requis"));
        }
        if (request.getContent() == null || request.getContent().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le contenu est requis"));
        }

        try {
            // Récupérer tous les abonnés actifs
            List<NewsletterSubscriber> subscribers = newsletterSubscriberRepository.findAll()
                    .stream()
                    .filter(NewsletterSubscriber::isActive)
                    .filter(NewsletterSubscriber::isConsentGiven)
                    .toList();

            if (subscribers.isEmpty()) {
                return ResponseEntity.badRequest().body(Map.of("error", "Aucun abonné actif trouvé"));
            }

            // Envoyer les emails
            int sentCount = 0;
            String senderEmail = authentication.getName(); // Email de l'employé/admin
            
            for (NewsletterSubscriber subscriber : subscribers) {
                try {
                    emailService.sendNewsletterEmail(
                            subscriber.getEmail(),
                            request.getSubject().trim(),
                            request.getContent().trim()
                    );
                    sentCount++;
                } catch (Exception e) {
                    // Continuer même si un email échoue
                    System.err.println("Erreur envoi à " + subscriber.getEmail() + ": " + e.getMessage());
                }
            }

            // Enregistrer dans l'historique
            NewsletterEmail newsletterEmail = NewsletterEmail.builder()
                    .subject(request.getSubject().trim())
                    .content(request.getContent().trim())
                    .recipientCount(sentCount)
                    .sentBy(senderEmail)
                    .build();
            newsletterEmailRepository.save(newsletterEmail);

            return ResponseEntity.ok(Map.of(
                    "message", "Email envoyé avec succès",
                    "sentCount", sentCount,
                    "totalSubscribers", subscribers.size()
            ));

        } catch (Exception e) {
            return ResponseEntity.status(500).body(Map.of(
                    "error", "Erreur lors de l'envoi de l'email : " + e.getMessage()
            ));
        }
    }

    @Operation(
            summary = "Récupérer l'historique des emails envoyés",
            description = """
            Récupère la liste paginée des emails newsletter envoyés.
            Accessible uniquement aux administrateurs et employés.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Historique récupéré avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @GetMapping("/emails")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> getEmailHistory(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "sentAt"));
        Page<NewsletterEmail> emails = newsletterEmailRepository.findAllByOrderBySentAtDesc(pageable);
        
        return ResponseEntity.ok(Map.of(
                "content", emails.getContent(),
                "totalElements", emails.getTotalElements(),
                "totalPages", emails.getTotalPages(),
                "size", emails.getSize(),
                "number", emails.getNumber(),
                "first", emails.isFirst(),
                "last", emails.isLast()
        ));
    }

    @Operation(
            summary = "Prévisualiser l'email",
            description = """
            Génère le HTML de prévisualisation de l'email sans l'envoyer.
            Accessible uniquement aux administrateurs et employés.
            """,
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "Contenu de l'email",
                    required = true,
                    content = @Content(
                            schema = @Schema(implementation = SendNewsletterRequest.class)
                    )
            ),
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Prévisualisation générée avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @PostMapping("/preview")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> previewEmail(@RequestBody SendNewsletterRequest request) {
        if (request.getSubject() == null || request.getSubject().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le sujet est requis"));
        }
        if (request.getContent() == null || request.getContent().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le contenu est requis"));
        }

        // Utiliser le service EmailService pour générer le template
        // On va créer une méthode publique pour ça
        String htmlContent = emailService.buildNewsletterTemplate(
                request.getSubject().trim(),
                request.getContent().trim()
        );

        return ResponseEntity.ok(Map.of("html", htmlContent));
    }

    @Data
    public static class NewsletterSubscribeRequest {
        @Schema(description = "Adresse email", example = "user@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
        private String email;
    }

    @Data
    public static class SendNewsletterRequest {
        @Schema(description = "Sujet de l'email", example = "Nouvelle offre spéciale !", requiredMode = Schema.RequiredMode.REQUIRED)
        private String subject;

        @Schema(description = "Contenu de l'email (texte simple)", example = "Découvrez nos nouveaux thés...", requiredMode = Schema.RequiredMode.REQUIRED)
        private String content;
    }
}