UserController.java

package com.paymybuddy.paymybuddy.controller;

import java.math.BigDecimal;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.paymybuddy.paymybuddy.dto.BalanceOperationDTO;
import com.paymybuddy.paymybuddy.dto.BuddiesDTO;
import com.paymybuddy.paymybuddy.dto.BuddyConnectionDTO;
import com.paymybuddy.paymybuddy.dto.BuddyForTransferDTO;
import com.paymybuddy.paymybuddy.dto.TransactionInListDTO;
import com.paymybuddy.paymybuddy.dto.TransactionRequestDTO;
import com.paymybuddy.paymybuddy.dto.UpdateUserDTO;
import com.paymybuddy.paymybuddy.model.User;
import com.paymybuddy.paymybuddy.service.TransactionService;
import com.paymybuddy.paymybuddy.service.UserService;

import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@RequestMapping("/user")
@AllArgsConstructor
@Slf4j
public class UserController {

    @Autowired
    private final UserService userService;

    @Autowired
    private final TransactionService transactionService;

    @GetMapping("/deposit")
    public String showDepositPage(Model model) {
        log.debug("- GET /user/deposit");

        model.addAttribute("balanceOperationDTO", new BalanceOperationDTO("", BigDecimal.ZERO));
        
        return "deposit";
    }
    
    @PostMapping("/deposit")
    public String deposit(@ModelAttribute BalanceOperationDTO operation, @AuthenticationPrincipal UserDetails userDetails, RedirectAttributes redirectAttributes) {
        log.debug("- POST /user/deposit: {}", operation);
        
        String userEmail = userDetails.getUsername();
        
        try {
            userService.deposit(new BalanceOperationDTO(userEmail, operation.amount()));
            redirectAttributes.addFlashAttribute("successMessage", "Argent ajouté");
        } catch (Exception e) {
            redirectAttributes.addFlashAttribute("errorMessage", "Erreur dépôt");
        }
        
        return "redirect:/user/transfer";
    }

    @GetMapping("/profile")
    public String getProfile(Model model, @AuthenticationPrincipal UserDetails userDetails) {
        
        String email = userDetails.getUsername();
        log.debug("- GET /user/profile: {}", email);

        User user = userService.getUserByEmail(email);
        String username = user.getUsername();
        String userEmail = user.getEmail();

        model.addAttribute("username", username);
        model.addAttribute("email", userEmail);
        model.addAttribute("editMode", false);

        return "profile";
    }

    @GetMapping("/profile/edit")
    public String editProfile(Model model, @AuthenticationPrincipal UserDetails userDetails) {
        
        String email = userDetails.getUsername();
        log.debug("- GET /user/profile/edit: {}", email);

        User user = userService.getUserByEmail(email);

        UpdateUserDTO updateUserDTO = new UpdateUserDTO();
        updateUserDTO.setId(user.getId());
        updateUserDTO.setUsername(user.getUsername());
        updateUserDTO.setEmail(user.getEmail());
        updateUserDTO.setPassword("");

        model.addAttribute("updateUserDTO", updateUserDTO);
        model.addAttribute("editMode", true);

        return "profile";
    }

    @PatchMapping("/profile")
    public String updateProfile(@Valid UpdateUserDTO updateUserDTO, 
                                BindingResult bindingResult,
                                @AuthenticationPrincipal UserDetails userDetails,
                                RedirectAttributes redirectAttributes
                                ) {
        log.debug("- PATCH /user/profile: {}", updateUserDTO);

        if (bindingResult.hasErrors()) {
            log.error("Validation errors: {}", bindingResult.getAllErrors());
            bindingResult.getFieldErrors().forEach(error -> 
                redirectAttributes.addFlashAttribute(error.getField() + "Error", error.getDefaultMessage()));
            return "redirect:/user/profile/edit";
        }

        //hors validation classique car pas d'update du mot de passe si vide
        String updatePassword = updateUserDTO.getPassword();
        if (updatePassword != null && !updatePassword.isEmpty()) {
            if (updatePassword.length() < 3) {
                log.error("- Password too short: {}", updatePassword);
                redirectAttributes.addFlashAttribute("errorMessage", "Le mot de passe doit contenir au moins 3 caractères");
                return "redirect:/user/profile/edit";
            }
        }
  
        User user = userService.getUserByEmail(userDetails.getUsername());

        userService.updateUser(new UpdateUserDTO(user.getId(), updateUserDTO.getUsername(), updateUserDTO.getEmail(), updateUserDTO.getPassword()));
        redirectAttributes.addFlashAttribute("successMessage", "Profil mis à jour");
        
        return "redirect:/user/profile";
    }

    @PostMapping("/transfer")
    public String handleTransfer(@RequestParam("buddy") String buddyEmail,
                                @RequestParam("amount") BigDecimal amount,
                                @RequestParam("description") String description,
                                Model model,
                                RedirectAttributes redirectAttributes,
                                @AuthenticationPrincipal UserDetails userDetails) {
        
        String email = userDetails.getUsername();
        TransactionRequestDTO transaction = new TransactionRequestDTO(email, buddyEmail, amount, description);
        log.debug("- POST /user/transfer: {}", transaction);

        transactionService.createTransaction(transaction);
        redirectAttributes.addFlashAttribute("successMessage", "Transfert réussi");

        return "redirect:/user/transfer";
    }

    @GetMapping("/transfer")
    public String showTransferForm( Model model,
                                    @AuthenticationPrincipal UserDetails userDetails,
                                    @RequestParam(defaultValue = "0") int page,
                                    @RequestParam(defaultValue = "8") int size
                                    ) {
        
        String email = userDetails.getUsername();
        log.debug("- GET /user/transfer: {}", email);

        User user = userService.getUserByEmail(email);
        BigDecimal balance = user.getBalance();
        
        Page<TransactionInListDTO> transactionsPage = userService.getTransactionsPaginated(user.getId(), page, size);
        if (page < 0 || page >= transactionsPage.getTotalPages()) {
            page = 0;
        }
        BuddiesDTO buddies = new BuddiesDTO(user.getBuddies().stream()
                                .map(buddy -> new BuddyForTransferDTO(buddy.getId(), buddy.getUsername(), buddy.getEmail()))
                                .collect(Collectors.toSet()));

        model.addAttribute("balance", balance);
        model.addAttribute("transactions", transactionsPage);
        model.addAttribute("buddies", buddies);
        model.addAttribute("currentPage", transactionsPage.getNumber());
        model.addAttribute("totalPages", transactionsPage.getTotalPages());
        
        return "transfer";
    }

    @PostMapping("/relation")
    public String addBuddy(@RequestParam("buddyEmail") String buddyEmail, @AuthenticationPrincipal UserDetails userDetails, RedirectAttributes redirectAttributes){
        
        String userEmail = userDetails.getUsername();
        BuddyConnectionDTO buddyConnectionDTO = new BuddyConnectionDTO(userEmail, buddyEmail);
        log.debug("- POST /user/relation: {}", buddyConnectionDTO);

        userService.addBuddy(buddyConnectionDTO);
        redirectAttributes.addFlashAttribute("successMessage", "Buddy ajouté avec succès");

        return "redirect:/user/relation";
    }

    @GetMapping("/relation")
    public String showRelationForm(Model model, @AuthenticationPrincipal UserDetails userDetails) {
        
        String email = userDetails.getUsername();
        log.debug("- GET /user/relation: {}", email);

        User user = userService.getUserByEmail(email);
        BuddiesDTO buddies = userService.getBuddies(user.getId());

        model.addAttribute("buddies", buddies);

        return "relation";
    }

    @DeleteMapping("/relation/{id}")
    public String removeBuddy(@PathVariable int id, @AuthenticationPrincipal UserDetails userDetails) {
        
        BuddyConnectionDTO buddyConnection = new BuddyConnectionDTO(userDetails.getUsername(), userService.getUserById(id).email());
        log.debug("- DELETE /user/relation", buddyConnection);

        userService.removeBuddy(buddyConnection);
        
        return "redirect:/user/relation";
    }
}