LoanController.java

package com.student_loan.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;
import com.student_loan.dtos.LoanRecord;
import com.student_loan.model.Loan;
import com.student_loan.model.User;
import com.student_loan.service.LoanService;
import com.student_loan.service.UserService;

import java.sql.Date;
import java.util.ArrayList;
import java.util.Objects;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Controller class for managing loans in the system. Handles HTTP requests
 * related to loan operations such as creating, updating, deleting, and
 * retrieving loans.
 */
@RestController
@RequestMapping("/loans")
public class LoanController {
    @Autowired
    private LoanService loanService;
    @Autowired
    private UserService userService;

    private static final Logger logger = LoggerFactory.getLogger(ItemController.class);

    /**
     * Retrieves the authenticated user from the SecurityContext.
     *
     * @return The authenticated user.
     */
	private User getAuthenticatedUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String email = authentication.getName();
        return userService.getUserByEmail(email);
    }

	 /**
     * Retrieves all loans in the system.
     *
     * @param token The authentication token.
     * @return ResponseEntity containing a list of all loans.
     */
    @GetMapping
    public ResponseEntity<List<Loan>> getAllLoans(@RequestParam("token") String token) {
    	User user = userService.getUserByToken(token);
        if (user == null || user.getAdmin()==false) {
        	return new ResponseEntity<>(new ArrayList<>(),HttpStatus.UNAUTHORIZED);
        }
        return  new ResponseEntity<>(loanService.getAllLoans(),HttpStatus.OK);
    }
    /**
     * Retrieves a loan by its ID.
     *
     * @param id    The ID of the loan.
     * @param token The authentication token.
     * @return ResponseEntity containing the loan.
     */
    
    @GetMapping("/{id}")
	public ResponseEntity<Loan> getLoanById(@PathVariable Long id, @RequestParam("token") String token) {
    	User user = userService.getUserByToken(token);
    	if (user == null || 
        	(!user.getAdmin() && 
        	user.getId() != loanService.getLoanById(id).get().getLender() && 
        	user.getId() != loanService.getLoanById(id).get().getBorrower())) {
        	return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
    	}

    	Optional<Loan> loanOpt = loanService.getLoanById(id);
    	if (loanOpt.isEmpty()) {
        	return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    	}
    	Loan loan = loanOpt.get();
    	return new ResponseEntity<>(loan, HttpStatus.OK);
	}

    /**
     * Retrieves loans by lender ID.
     *
     * @param token    The authentication token.
     * @param lenderId The ID of the lender.
     * @return ResponseEntity containing a list of loans by the lender.
     */
    @GetMapping("/lender")
    public ResponseEntity<List<Loan>> getLoansByLender() {
    	User user = getAuthenticatedUser();
		if (user == null) {
			return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
		}
        try {
			List<Loan> loans = loanService.getLoansByLender(user.getId());
			return new ResponseEntity<>(loans, HttpStatus.OK);
			
			} catch (RuntimeException e) {
				return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
	}
    
    /**
     * Retrieves loans by borrower ID.
     *
     * @param token       The authentication token.
     * @param borrowerId  The ID of the borrower.
     * @return ResponseEntity containing a list of loans by the borrower.
     */
    @GetMapping("/borrower")
	public ResponseEntity<List<Loan>> getLoansByBorrower() {

    	User user = getAuthenticatedUser();
		if (user == null) {
			return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
		}

		List<Loan> loans = loanService.getLoansByBorrower(user.getId());
		return new ResponseEntity<>(loans, HttpStatus.OK);
	}

    /**
     * Creates a new loan.
     *
     * @param loanRecord  The loan data.
     * @return ResponseEntity indicating the result of the operation.
     */
    @PostMapping("/create")
    public ResponseEntity<String> createLoan(@RequestBody LoanRecord loanRecord) {
    	User user = getAuthenticatedUser();
        if (user == null) {
			return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
		}
		
		Loan loanEntity = convertToLoan(loanRecord);
        loanEntity.setBorrower(user.getId());
		try {
			loanService.createLoan(loanEntity);
        } catch (RuntimeException e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
        }
		// If the loan is created successfully, return a 201 Created response
		return new ResponseEntity<>(HttpStatus.CREATED);
    }
    
    /**
     * Updates a loan by its ID.
     *
     * @param id        The ID of the loan.
     * @param loan      The updated loan data.
     * @param authHeader The authorization header containing the token.
     * @return ResponseEntity indicating the result of the operation.
     */
    
    @PutMapping("/{id}")
	public ResponseEntity<String> updateLoan(@PathVariable Long id, @RequestBody LoanRecord loan, @RequestHeader("Authorization") String authHeader) {
		if (authHeader == null || !authHeader.startsWith("Bearer ")) {
        	return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
    	}
		
    	String token = authHeader.substring(7);

    	User user = userService.getUserByToken(token);
    	Loan existingLoan = loanService.getLoanById(id).get();
		if (user == null || user.getId()!=existingLoan.getLender() && user.getId()!=existingLoan.getBorrower() && user.getAdmin()==false) {
			return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
		}
		
    	try {
    		existingLoan.setLender(loan.lender()==null ? existingLoan.getLender() : loan.lender());
    		existingLoan.setBorrower(loan.borrower()==null ? existingLoan.getBorrower() : loan.borrower());
    		existingLoan.setItem(loan.item()==null ? existingLoan.getItem() : loan.item());
    		existingLoan.setLoanDate(loan.loanDate()==null ? existingLoan.getLoanDate() : Date.valueOf(loan.loanDate()));
    		existingLoan.setEstimatedReturnDate(loan.estimatedReturnDate()==null ? existingLoan.getEstimatedReturnDate() : Date.valueOf(loan.estimatedReturnDate()));
    		existingLoan.setRealReturnDate(loan.realReturnDate()==null ? existingLoan.getRealReturnDate() : Date.valueOf(loan.realReturnDate()));
    		existingLoan.setLoanStatus(loan.loanStatus()==null ? existingLoan.getLoanStatus() : Loan.Status.valueOf(loan.loanStatus()));
    		existingLoan.setRating(loan.rating()==null ? existingLoan.getRating() : Double.valueOf(loan.rating()));
    		existingLoan.setObservations(loan.observations()==null ? existingLoan.getObservations() : loan.observations());	
			loanService.saveLoan(existingLoan);
			return new ResponseEntity<>(HttpStatus.OK);
		} catch (RuntimeException e) {
			return new ResponseEntity<>(e.getMessage(),HttpStatus.NOT_FOUND);
		}
	}

    /**
     * Marks a loan as returned by its item ID.
     *
     * @param itemId The ID of the item.
     * @return ResponseEntity indicating the result of the operation.
     */
	@PutMapping("/{itemId}/return")
    public ResponseEntity<Void> returnLoan(@PathVariable Long itemId) {
		User user = getAuthenticatedUser();
		if (user == null) {
			return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
		}
		try {
			boolean updated = loanService.returnLoan(itemId, user.getId());
			if (updated) {
				return new ResponseEntity<>(HttpStatus.OK);
			} else {
				return new ResponseEntity<>(HttpStatus.NOT_FOUND);
			}
		} catch (Exception e) {
			return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
		}
    }

	
	 /**
     * Deletes a loan by its ID.
     *
     * @param id    The ID of the loan.
     * @param token The authentication token.
     * @return ResponseEntity indicating the result of the operation.
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<String> deleteLoan(@PathVariable Long id, @RequestParam("token") String token) {
    	User user = userService.getUserByToken(token);
        if (user == null || user.getId()!=loanService.getLoanById(id).get().getLender() && user.getId()!=loanService.getLoanById(id).get().getBorrower() && user.getAdmin()==false) {
        	 return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
        }
        try {
        	  loanService.deleteLoan(id);
              return new ResponseEntity<>(HttpStatus.OK);
		} catch (RuntimeException e) {
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		}
      
    }
    
    /**
     * Converts a LoanRecord to a Loan entity.
     *
     * @param loanRecord The loan record.
     * @return The converted Loan entity.
     */
	private Loan convertToLoan(LoanRecord loanRecord) {
        return new Loan(
        	null,
        	loanRecord.lender(),
            null, // It is setted in createLoan function
            loanRecord.item(),
            Date.valueOf(loanRecord.loanDate()),
            Date.valueOf(loanRecord.estimatedReturnDate()),
            null,
            Loan.Status.IN_USE,
            null,
            loanRecord.observations()
        );
	}
	
}