package dev.lions.btpxpress.application.service; import dev.lions.btpxpress.domain.core.entity.Chantier; import dev.lions.btpxpress.domain.core.entity.Client; import dev.lions.btpxpress.domain.core.entity.TypeClient; import dev.lions.btpxpress.domain.infrastructure.repository.ClientRepository; import dev.lions.btpxpress.domain.shared.dto.ClientCreateDTO; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.validation.Valid; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.NotFoundException; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Service de gestion des clients - Architecture 2025 MIGRATION: Préservation exacte de toutes les * logiques de validation et recherche */ @ApplicationScoped public class ClientService { private static final Logger logger = LoggerFactory.getLogger(ClientService.class); @Inject ClientRepository clientRepository; // === MÉTHODES DE RECHERCHE - PRÉSERVÉES EXACTEMENT === public List findAll() { logger.debug("Recherche de tous les clients actifs"); return clientRepository.findActifs(); } public List findAll(int page, int size) { logger.debug("Recherche des clients actifs - page: {}, taille: {}", page, size); return clientRepository.findActifs(page, size); } public Optional findById(UUID id) { logger.debug("Recherche du client avec l'ID: {}", id); return clientRepository.findByIdOptional(id); } public Client findByIdRequired(UUID id) { return findById(id) .orElseThrow(() -> new NotFoundException("Client non trouvé avec l'ID: " + id)); } public Optional findByEmail(String email) { logger.debug("Recherche du client avec l'email: {}", email); return clientRepository.findByEmail(email); } public List searchByNom(String nom) { logger.debug("Recherche des clients par nom: {}", nom); return clientRepository.findByNomContaining(nom); } public List findByEntreprise(String entreprise) { logger.debug("Recherche des clients par entreprise: {}", entreprise); return clientRepository.findByEntreprise(entreprise); } public List searchByEntreprise(String entreprise) { logger.debug("Recherche des clients par entreprise: {}", entreprise); return clientRepository.findByEntreprise(entreprise); } public List findByVille(String ville) { logger.debug("Recherche des clients par ville: {}", ville); return clientRepository.findByVille(ville); } public List findByCodePostal(String codePostal) { logger.debug("Recherche des clients par code postal: {}", codePostal); return clientRepository.findByCodePostal(codePostal); } public List findProfessionnels() { logger.debug("Recherche des clients professionnels"); return clientRepository.findByType(TypeClient.PROFESSIONNEL); } public List findParticuliers() { logger.debug("Recherche des clients particuliers"); return clientRepository.findByType(TypeClient.PARTICULIER); } public List findCreesRecemment(int jours) { logger.debug("Recherche des clients créés récemment: {} jours", jours); return clientRepository.findCreesRecemment(jours); } public List searchClients(String query) { logger.debug("Recherche de clients: {}", query); return clientRepository.findByNomContaining(query); } public Map getStatistiques() { logger.debug("Calcul des statistiques clients"); Map stats = new HashMap<>(); stats.put("total", count()); stats.put("professionnels", findProfessionnels().size()); stats.put("particuliers", findParticuliers().size()); stats.put("nouveaux", findCreesRecemment(30).size()); return stats; } public List> getHistoriqueChantiers(UUID clientId) { logger.debug("Historique des chantiers pour le client: {}", clientId); // Conversion des chantiers en Map pour l'API List chantiers = clientRepository.getHistoriqueChantiers(clientId); List> historique = new ArrayList<>(); for (Chantier chantier : chantiers) { Map chantierMap = new HashMap<>(); chantierMap.put("id", chantier.getId()); chantierMap.put("nom", chantier.getNom()); chantierMap.put("statut", chantier.getStatut()); chantierMap.put("dateDebut", chantier.getDateDebut()); chantierMap.put("dateFin", chantier.getDateFinPrevue()); chantierMap.put("montant", chantier.getMontantPrevu()); historique.add(chantierMap); } return historique; } public Map getDashboardClient(UUID id) { logger.debug("Dashboard du client: {}", id); Client client = findByIdRequired(id); // Récupération des statistiques via le repository Map stats = clientRepository.getClientStatistics(id); Map dashboard = new HashMap<>(); dashboard.put("client", client); dashboard.put("chantiersTotal", stats.getOrDefault("chantiersTotal", 0)); dashboard.put("chantiersEnCours", stats.getOrDefault("chantiersEnCours", 0)); dashboard.put( "chiffreAffairesTotal", stats.getOrDefault("chiffreAffairesTotal", BigDecimal.ZERO)); dashboard.put("devisEnAttente", stats.getOrDefault("devisEnAttente", 0)); dashboard.put("facturesImpayees", stats.getOrDefault("facturesImpayees", 0)); dashboard.put("derniereActivite", stats.getOrDefault("derniereActivite", null)); return dashboard; } public List searchByVille(String ville) { logger.debug("Recherche des clients par ville: {}", ville); return clientRepository.findByVille(ville); } // === MÉTHODES CRUD - LOGIQUES CRITIQUES PRÉSERVÉES === @Transactional public Client create(@Valid Client client) { logger.info("Création d'un nouveau client: {} {}", client.getPrenom(), client.getNom()); // Vérifications métier - LOGIQUE CRITIQUE PRÉSERVÉE validateClient(client); // Vérifier l'unicité de l'email - LOGIQUE CRITIQUE PRÉSERVÉE if (client.getEmail() != null && clientRepository.existsByEmail(client.getEmail())) { throw new BadRequestException("Un client avec cet email existe déjà"); } // Vérifier l'unicité du SIRET - LOGIQUE CRITIQUE PRÉSERVÉE if (client.getSiret() != null && clientRepository.existsBySiret(client.getSiret())) { throw new BadRequestException("Un client avec ce SIRET existe déjà"); } clientRepository.persist(client); logger.info("Client créé avec succès avec l'ID: {}", client.getId()); return client; } @Transactional public Client createFromDTO(@Valid ClientCreateDTO dto) { logger.info("Création d'un nouveau client depuis DTO: {} {}", dto.getPrenom(), dto.getNom()); try { // Créer l'entité Client - LOGIQUE EXACTE PRÉSERVÉE Client client = new Client(); client.setNom(dto.getNom()); client.setPrenom(dto.getPrenom()); client.setEntreprise(dto.getEntreprise()); client.setEmail(dto.getEmail()); client.setTelephone(dto.getTelephone()); client.setAdresse(dto.getAdresse()); client.setCodePostal(dto.getCodePostal()); client.setVille(dto.getVille()); client.setSiret(dto.getSiret()); client.setNumeroTVA(dto.getNumeroTVA()); client.setActif(dto.getActif() != null ? dto.getActif() : true); // Utiliser la méthode create existante return create(client); } catch (Exception e) { logger.error("Erreur lors de la création du client: {}", e.getMessage(), e); throw e; } } @Transactional public Client update(UUID id, @Valid Client clientData) { logger.info("Mise à jour du client avec l'ID: {}", id); Client existingClient = findByIdRequired(id); // Vérifications métier - LOGIQUE CRITIQUE PRÉSERVÉE validateClient(clientData); // Vérifier l'unicité de l'email (si changé) - LOGIQUE CRITIQUE PRÉSERVÉE if (clientData.getEmail() != null && !clientData.getEmail().equals(existingClient.getEmail())) { if (clientRepository.existsByEmail(clientData.getEmail())) { throw new BadRequestException("Un client avec cet email existe déjà"); } } // Vérifier l'unicité du SIRET (si changé) - LOGIQUE CRITIQUE PRÉSERVÉE if (clientData.getSiret() != null && !clientData.getSiret().equals(existingClient.getSiret())) { if (clientRepository.existsBySiret(clientData.getSiret())) { throw new BadRequestException("Un client avec ce SIRET existe déjà"); } } // Mise à jour des champs updateClientFields(existingClient, clientData); existingClient.setDateModification(LocalDateTime.now()); clientRepository.persist(existingClient); logger.info("Client mis à jour avec succès"); return existingClient; } @Transactional public void delete(UUID id) { logger.info("Suppression logique du client avec l'ID: {}", id); Client client = findByIdRequired(id); clientRepository.softDelete(id); logger.info("Client supprimé avec succès"); } @Transactional public void deleteByEmail(String email) { logger.info("Suppression logique du client avec l'email: {}", email); Client client = findByEmail(email) .orElseThrow(() -> new NotFoundException("Client non trouvé avec l'email: " + email)); clientRepository.softDeleteByEmail(email); logger.info("Client supprimé avec succès"); } // === MÉTHODES DE COMPTAGE - PRÉSERVÉES EXACTEMENT === public long count() { return clientRepository.countActifs(); } // === MÉTHODES PRIVÉES DE VALIDATION - LOGIQUES CRITIQUES PRÉSERVÉES EXACTEMENT === /** Validation complète du client - RÈGLES MÉTIER PRÉSERVÉES */ private void validateClient(Client client) { if (client.getNom() == null || client.getNom().trim().isEmpty()) { throw new BadRequestException("Le nom du client est obligatoire"); } if (client.getPrenom() == null || client.getPrenom().trim().isEmpty()) { throw new BadRequestException("Le prénom du client est obligatoire"); } } /** Mise à jour des champs client - LOGIQUE EXACTE PRÉSERVÉE */ private void updateClientFields(Client existing, Client updated) { existing.setNom(updated.getNom()); existing.setPrenom(updated.getPrenom()); existing.setEntreprise(updated.getEntreprise()); existing.setEmail(updated.getEmail()); existing.setTelephone(updated.getTelephone()); existing.setAdresse(updated.getAdresse()); existing.setCodePostal(updated.getCodePostal()); existing.setVille(updated.getVille()); existing.setNumeroTVA(updated.getNumeroTVA()); existing.setSiret(updated.getSiret()); existing.setActif(updated.getActif()); } }