package handlers import ( "log" "net/http" "strconv" "time" "finance/backend/internal/database" "finance/backend/internal/models" "github.com/gin-gonic/gin" ) // LoanHandler handles loan-related operations type LoanHandler struct { } // NewLoanHandler creates and returns a new LoanHandler instance func NewLoanHandler() *LoanHandler { return &LoanHandler{} } // GetLoans retrieves all loans for the authenticated user func (h *LoanHandler) GetLoans(c *gin.Context) { userID := c.MustGet("userID").(uint) var loans []models.Loan if err := database.DB.Where("user_id = ?", userID).Find(&loans).Error; err != nil { log.Printf("Error fetching loans for user %d: %v", userID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get loans"}) return } c.JSON(http.StatusOK, loans) } // GetLoanByID retrieves a specific loan by ID for the authenticated user func (h *LoanHandler) GetLoanByID(c *gin.Context) { userID := c.MustGet("userID").(uint) loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) return } var loan models.Loan if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) return } c.JSON(http.StatusOK, loan) } // CreateLoan creates a new loan for the authenticated user func (h *LoanHandler) CreateLoan(c *gin.Context) { userID := c.MustGet("userID").(uint) // Define input structure type CreateLoanInput struct { Name string `json:"name" binding:"required"` OriginalAmount int64 `json:"originalAmount" binding:"required"` CurrentBalance int64 `json:"currentBalance" binding:"required"` InterestRate float64 `json:"interestRate"` StartDate string `json:"startDate"` EndDate string `json:"endDate"` AccountID *uint `json:"accountId"` } var input CreateLoanInput if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Parse dates if provided var startDate, endDate time.Time var err error if input.StartDate != "" { startDate, err = time.Parse("2006-01-02", input.StartDate) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start date format. Use YYYY-MM-DD"}) return } } if input.EndDate != "" { endDate, err = time.Parse("2006-01-02", input.EndDate) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end date format. Use YYYY-MM-DD"}) return } } // Validate account if provided if input.AccountID != nil { var account models.Account if err := database.DB.Where("id = ? AND user_id = ?", *input.AccountID, userID).First(&account).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Associated account not found or doesn't belong to user"}) return } } // Create loan loan := models.Loan{ UserID: userID, AccountID: input.AccountID, Name: input.Name, OriginalAmount: input.OriginalAmount, CurrentBalance: input.CurrentBalance, InterestRate: input.InterestRate, } // Only set dates if they were provided if !startDate.IsZero() { loan.StartDate = startDate } if !endDate.IsZero() { loan.EndDate = endDate } if err := database.DB.Create(&loan).Error; err != nil { log.Printf("Error creating loan for user %d: %v", userID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create loan"}) return } c.JSON(http.StatusCreated, loan) } // UpdateLoan updates an existing loan for the authenticated user func (h *LoanHandler) UpdateLoan(c *gin.Context) { userID := c.MustGet("userID").(uint) loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) return } // Find loan var loan models.Loan if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) return } // Define update structure type UpdateLoanInput struct { Name string `json:"name"` OriginalAmount int64 `json:"originalAmount"` CurrentBalance int64 `json:"currentBalance"` InterestRate float64 `json:"interestRate"` StartDate string `json:"startDate"` EndDate string `json:"endDate"` AccountID *uint `json:"accountId"` } var input UpdateLoanInput if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Update fields if provided if input.Name != "" { loan.Name = input.Name } if input.OriginalAmount != 0 { loan.OriginalAmount = input.OriginalAmount } if input.CurrentBalance != 0 { loan.CurrentBalance = input.CurrentBalance } if input.InterestRate != 0 { loan.InterestRate = input.InterestRate } if input.StartDate != "" { startDate, err := time.Parse("2006-01-02", input.StartDate) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start date format. Use YYYY-MM-DD"}) return } loan.StartDate = startDate } if input.EndDate != "" { endDate, err := time.Parse("2006-01-02", input.EndDate) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end date format. Use YYYY-MM-DD"}) return } loan.EndDate = endDate } if input.AccountID != nil { // Validate account if provided var account models.Account if err := database.DB.Where("id = ? AND user_id = ?", *input.AccountID, userID).First(&account).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Associated account not found or doesn't belong to user"}) return } loan.AccountID = input.AccountID } // Save changes if err := database.DB.Save(&loan).Error; err != nil { log.Printf("Error updating loan ID %d for user %d: %v", loanID, userID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update loan"}) return } c.JSON(http.StatusOK, loan) } // DeleteLoan deletes a loan belonging to the authenticated user func (h *LoanHandler) DeleteLoan(c *gin.Context) { userID := c.MustGet("userID").(uint) loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) return } // Check if loan exists and belongs to user var loan models.Loan if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) return } // Delete loan if err := database.DB.Delete(&loan).Error; err != nil { log.Printf("Error deleting loan ID %d for user %d: %v", loanID, userID, err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete loan"}) return } c.JSON(http.StatusOK, gin.H{"message": "Loan deleted successfully"}) }