ContactController.java

package com.archiweb.api;

import com.archiweb.model.ContactMessage;
import com.archiweb.model.ContactMessageReply;
import com.archiweb.repository.ContactMessageRepository;
import com.archiweb.repository.ContactMessageReplyRepository;
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.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@RestController
@RequestMapping("/api/contact")
@RequiredArgsConstructor
@Tag(
        name = "Contact",
        description = """
        Gestion des messages de contact.
        Permet aux utilisateurs d'envoyer des messages de contact et aux administrateurs de les consulter.
        """
)
public class ContactController {

    private final ContactMessageRepository contactMessageRepository;
    private final ContactMessageReplyRepository contactMessageReplyRepository;
    private final EmailService emailService;

    @Operation(
            summary = "Envoyer un message de contact",
            description = """
            Permet à un utilisateur d'envoyer un message de contact.
            Le message sera stocké en base de données et pourra être consulté par les administrateurs.
            """,
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "Données du message de contact",
                    required = true,
                    content = @Content(
                            schema = @Schema(implementation = ContactRequest.class),
                            examples = @ExampleObject(
                                    value = """
                                    {
                                      "name": "Jean Dupont",
                                      "email": "jean.dupont@example.com",
                                      "message": "Bonjour, j'aimerais avoir des informations sur vos produits."
                                    }
                                    """
                            )
                    )
            ),
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Message envoyé avec succès",
                            content = @Content(
                                    mediaType = "application/json",
                                    examples = @ExampleObject(
                                            value = """
                                            {
                                              "message": "Votre message a été envoyé avec succès. Nous vous répondrons dans les plus brefs délais."
                                            }
                                            """
                                    )
                            )
                    ),
                    @ApiResponse(
                            responseCode = "400",
                            description = "Données invalides"
                    )
            }
    )
    @PostMapping("/send")
    public ResponseEntity<?> sendMessage(@RequestBody ContactRequest request) {
        // Validation
        if (request.getName() == null || request.getName().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le nom est requis"));
        }
        if (request.getEmail() == null || request.getEmail().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "L'email est requis"));
        }
        if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le message est requis"));
        }

        // Créer et sauvegarder le message
        ContactMessage contactMessage = ContactMessage.builder()
                .name(request.getName().trim())
                .email(request.getEmail().trim().toLowerCase())
                .message(request.getMessage().trim())
                .read(false)
                .createdAt(LocalDateTime.now())
                .build();

        contactMessageRepository.save(contactMessage);

        return ResponseEntity.ok(Map.of(
                "message", "Votre message a été envoyé avec succès. Nous vous répondrons dans les plus brefs délais."
        ));
    }

    @Operation(
            summary = "Récupérer tous les messages de contact (paginé)",
            description = """
            Récupère tous les messages de contact avec pagination.
            Accessible uniquement aux administrateurs.
            Les messages sont triés par date de création (plus récents en premier).
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Liste des messages récupérée avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs"
                    )
            }
    )
    @GetMapping("/messages")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<Page<ContactMessage>> getAllMessages(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size
    ) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        Page<ContactMessage> messages = contactMessageRepository.findAllByOrderByCreatedAtDesc(pageable);
        return ResponseEntity.ok(messages);
    }

    @Operation(
            summary = "Récupérer les messages non lus",
            description = """
            Récupère tous les messages de contact qui n'ont pas encore été lus.
            Accessible uniquement aux administrateurs.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Liste des messages non lus récupérée avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs"
                    )
            }
    )
    @GetMapping("/messages/unread")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<List<ContactMessage>> getUnreadMessages() {
        List<ContactMessage> messages = contactMessageRepository.findByReadFalseOrderByCreatedAtDesc();
        return ResponseEntity.ok(messages);
    }

    @Operation(
            summary = "Compter les messages non lus",
            description = """
            Retourne le nombre de messages non lus.
            Accessible uniquement aux administrateurs.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Nombre de messages non lus"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs"
                    )
            }
    )
    @GetMapping("/messages/unread/count")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<Map<String, Long>> getUnreadCount() {
        long count = contactMessageRepository.countByReadFalse();
        return ResponseEntity.ok(Map.of("count", count));
    }

    @Operation(
            summary = "Marquer un message comme lu",
            description = """
            Marque un message de contact comme lu.
            Accessible uniquement aux administrateurs.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Message marqué comme lu"
                    ),
                    @ApiResponse(
                            responseCode = "404",
                            description = "Message non trouvé"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs"
                    )
            }
    )
    @PatchMapping("/messages/{id}/read")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> markAsRead(@PathVariable Long id) {
        Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
        if (messageOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }

        ContactMessage message = messageOpt.get();
        message.setRead(true);
        message.setReadAt(LocalDateTime.now());
        contactMessageRepository.save(message);

        return ResponseEntity.ok(Map.of("message", "Message marqué comme lu"));
    }

    @Operation(
            summary = "Récupérer un message par ID",
            description = """
            Récupère un message de contact par son ID.
            Accessible uniquement aux administrateurs.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Message récupéré avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "404",
                            description = "Message non trouvé"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs"
                    )
            }
    )
    @GetMapping("/messages/{id}")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<ContactMessage> getMessage(@PathVariable Long id) {
        Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
        if (messageOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(messageOpt.get());
    }

    @Operation(
            summary = "Récupérer la conversation complète",
            description = """
            Récupère le message original et toutes les réponses associées, dans l'ordre chronologique.
            Accessible uniquement aux administrateurs et employés.
            """,
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Conversation récupérée avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "404",
                            description = "Message non trouvé"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @GetMapping("/messages/{id}/conversation")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> getConversation(@PathVariable Long id) {
        Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
        if (messageOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }

        ContactMessage message = messageOpt.get();
        List<ContactMessageReply> replies = contactMessageReplyRepository.findByContactMessageIdOrderByCreatedAtAsc(id);

        Map<String, Object> conversation = Map.of(
                "originalMessage", Map.of(
                        "id", message.getId(),
                        "name", message.getName(),
                        "email", message.getEmail(),
                        "message", message.getMessage(),
                        "createdAt", message.getCreatedAt(),
                        "isFromUser", true
                ),
                "replies", replies.stream().map(reply -> Map.of(
                        "id", reply.getId(),
                        "subject", reply.getSubject(),
                        "message", reply.getMessage(),
                        "isFromUser", reply.isFromUser(),
                        "createdAt", reply.getCreatedAt()
                )).toList()
        );

        return ResponseEntity.ok(conversation);
    }

    @Operation(
            summary = "Répondre à un message de contact",
            description = """
            Envoie une réponse par email à l'expéditeur d'un message de contact.
            Accessible uniquement aux administrateurs et employés.
            """,
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                    description = "Contenu de la réponse",
                    required = true,
                    content = @Content(
                            schema = @Schema(implementation = ReplyRequest.class),
                            examples = @ExampleObject(
                                    value = """
                                    {
                                      "subject": "Re: Votre demande",
                                      "message": "Bonjour, merci pour votre message. Nous vous répondrons dans les plus brefs délais."
                                    }
                                    """
                            )
                    )
            ),
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Réponse envoyée avec succès"
                    ),
                    @ApiResponse(
                            responseCode = "404",
                            description = "Message non trouvé"
                    ),
                    @ApiResponse(
                            responseCode = "403",
                            description = "Accès refusé - Réservé aux administrateurs et employés"
                    )
            }
    )
    @PostMapping("/messages/{id}/reply")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_EMPLOYEE')")
    public ResponseEntity<?> replyToMessage(@PathVariable Long id, @RequestBody ReplyRequest request) {
        Optional<ContactMessage> messageOpt = contactMessageRepository.findById(id);
        if (messageOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }

        ContactMessage contactMessage = messageOpt.get();

        // Validation
        if (request.getSubject() == null || request.getSubject().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le sujet est requis"));
        }
        if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("error", "Le message est requis"));
        }

        try {
            // Construire le message de réponse avec le message original
            String replyMessage = String.format(
                "Bonjour %s,\n\n%s\n\n---\nMessage original :\n%s",
                contactMessage.getName(),
                request.getMessage().trim(),
                contactMessage.getMessage()
            );

            // Sauvegarder la réponse dans la base de données
            ContactMessageReply reply = ContactMessageReply.builder()
                    .contactMessage(contactMessage)
                    .subject(request.getSubject().trim())
                    .message(request.getMessage().trim())
                    .isFromUser(false) // Réponse de l'admin/employé
                    .build();
            contactMessageReplyRepository.save(reply);

            // Envoyer l'email de réponse
            emailService.sendEmail(
                contactMessage.getEmail(),
                request.getSubject().trim(),
                replyMessage
            );

            return ResponseEntity.ok(Map.of(
                "message", "Réponse envoyée avec succès à " + contactMessage.getEmail(),
                "replyId", reply.getId()
            ));
        } catch (Exception e) {
            return ResponseEntity.status(500).body(Map.of(
                "error", "Erreur lors de l'envoi de la réponse : " + e.getMessage()
            ));
        }
    }

    @Data
    public static class ContactRequest {
        @Schema(description = "Nom de l'expéditeur", example = "Jean Dupont", requiredMode = Schema.RequiredMode.REQUIRED)
        private String name;

        @Schema(description = "Email de l'expéditeur", example = "jean.dupont@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
        private String email;

        @Schema(description = "Message", example = "Bonjour, j'aimerais avoir des informations sur vos produits.", requiredMode = Schema.RequiredMode.REQUIRED)
        private String message;
    }

    @Data
    public static class ReplyRequest {
        @Schema(description = "Sujet de la réponse", example = "Re: Votre demande", requiredMode = Schema.RequiredMode.REQUIRED)
        private String subject;

        @Schema(description = "Message de réponse", example = "Bonjour, merci pour votre message...", requiredMode = Schema.RequiredMode.REQUIRED)
        private String message;
    }
}