package handlers import ( "fmt" "strconv" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) type TransferWalletRes struct { ID int64 `json:"id"` Amount float32 `json:"amount"` Verified bool `json:"verified"` Message string `json:"message"` Type string `json:"type"` PaymentMethod string `json:"payment_method"` ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"` SenderWalletID *int64 `json:"sender_wallet_id,omitempty"` DepositorID *int64 `json:"depositor_id,omitempty"` DepositorFirstName string `json:"depositor_first_name"` DepositorLastName string `json:"depositor_last_name"` DepositorPhoneNumber string `json:"depositor_phone_number"` ReferenceNumber string `json:"reference_number"` // ← Add this CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type RefillRes struct { ID int64 `json:"id" example:"1"` Amount float32 `json:"amount" example:"100.0"` Verified bool `json:"verified" example:"true"` Type string `json:"type" example:"transfer"` PaymentMethod string `json:"payment_method" example:"bank"` ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"` SenderWalletID *int64 `json:"sender_wallet_id" example:"1"` CashierID *int64 `json:"cashier_id" example:"789"` CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"` } func convertTransfer(t domain.Transfer) TransferWalletRes { var receiverID *int64 if t.ReceiverWalletID.Valid { receiverID = &t.ReceiverWalletID.Value } var senderID *int64 if t.SenderWalletID.Valid { senderID = &t.SenderWalletID.Value } var depositorID *int64 if t.DepositorID.Valid { depositorID = &t.DepositorID.Value } return TransferWalletRes{ ID: t.ID, Amount: t.Amount.Float32(), Verified: t.Verified, Message: t.Message, Type: string(t.Type), PaymentMethod: string(t.PaymentMethod), ReceiverWalletID: receiverID, SenderWalletID: senderID, DepositorID: depositorID, ReferenceNumber: t.ReferenceNumber, CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, } } func convertTransferDetail(t domain.TransferDetail) TransferWalletRes { var receiverID *int64 if t.ReceiverWalletID.Valid { receiverID = &t.ReceiverWalletID.Value } var senderID *int64 if t.SenderWalletID.Valid { senderID = &t.SenderWalletID.Value } var depositorID *int64 if t.DepositorID.Valid { depositorID = &t.DepositorID.Value } return TransferWalletRes{ ID: t.ID, Amount: t.Amount.Float32(), Verified: t.Verified, Message: t.Message, Type: string(t.Type), PaymentMethod: string(t.PaymentMethod), ReceiverWalletID: receiverID, SenderWalletID: senderID, DepositorID: depositorID, DepositorFirstName: t.DepositorFirstName, DepositorLastName: t.DepositorLastName, DepositorPhoneNumber: t.DepositorPhoneNumber, ReferenceNumber: t.ReferenceNumber, CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, } } type CreateTransferReq struct { Amount float32 `json:"amount" example:"100.0"` PaymentMethod string `json:"payment_method" example:"cash"` } type CreateRefillReq struct { Amount float32 `json:"amount" example:"100.0"` } // GetTransfersByWallet godoc // @Summary Get transfer by wallet // @Description Get transfer by wallet // @Tags transfer // @Accept json // @Produce json // @Param transferToWallet body CreateTransferReq true "Create Transfer" // @Success 200 {object} TransferWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /transfer/wallet/{id} [get] func (h *Handler) GetTransfersByWallet(c *fiber.Ctx) error { walletID := c.Params("id") id, err := strconv.ParseInt(walletID, 10, 64) if err != nil { h.logger.Error("Invalid wallet ID", "walletID", walletID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil) } transfers, err := h.walletSvc.GetTransfersByWallet(c.Context(), int64(id)) if err != nil { h.logger.Error("Failed to get transfers by wallet", "walletID", walletID, "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transfers", err, nil) } var transferResponses []TransferWalletRes for _, transfer := range transfers { transferResponses = append(transferResponses, convertTransferDetail(transfer)) } return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil) } // TransferToWallet godoc // @Summary Create a transfer to wallet // @Description Create a transfer to wallet // @Tags transfer // @Accept json // @Produce json // @Param transferToWallet body CreateTransferReq true "Create Transfer" // @Success 200 {object} TransferWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /transfer/wallet/:id [post] func (h *Handler) TransferToWallet(c *fiber.Ctx) error { receiverIDString := c.Params("id") receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) if err != nil { h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil) } // Get sender ID from the cashier userID := c.Locals("user_id").(int64) role := c.Locals("role").(domain.Role) companyID := c.Locals("company_id").(domain.ValidInt64) fmt.Printf("\n\nCompany ID: %v\n\n", companyID.Value) var senderID int64 //TODO: check to make sure that the cashiers aren't transferring TO branch wallet switch role { case domain.RoleCustomer: h.logger.Error("Unauthorized access", "userID", userID, "role", role) return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) case domain.RoleAdmin: company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to fetch company", Error: err.Error(), }) // return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching company", err, nil) } senderID = company.WalletID h.logger.Error("Will", "userID", userID, "role", role) case domain.RoleSuperAdmin: return response.WriteJSON(c, fiber.StatusBadRequest, "Super Admin does not have a wallet", err, nil) case domain.RoleBranchManager: return response.WriteJSON(c, fiber.StatusBadRequest, "Branch Manager does not have a wallet", err, nil) case domain.RoleCashier: cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID) if err != nil { h.logger.Error("Failed to get branch", "user ID", userID, "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil) } senderID = cashierBranch.WalletID default: return response.WriteJSON(c, fiber.StatusInternalServerError, "Unknown Role", err, nil) } var req CreateTransferReq if err := c.BodyParser(&req); err != nil { h.logger.Error("CreateTransferReq failed", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := h.validator.Validate(c, req) if !ok { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transfer, err := h.walletSvc.TransferToWallet(c.Context(), senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod), domain.ValidInt64{Value: userID, Valid: true}, fmt.Sprintf("Transferred %v from wallet to another", req.Amount), ) if err != nil { h.mongoLoggerSvc.Error("Failed to transfer money to wallet", zap.Error(err)) return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil) } res := convertTransfer(transfer) return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) } // RefillWallet godoc // @Summary Refill wallet // @Description Super Admin route to refill a wallet // @Tags transfer // @Accept json // @Produce json // @Param refillWallet body CreateTransferReq true "Create Transfer" // @Success 200 {object} TransferWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /transfer/refill/:id [post] func (h *Handler) RefillWallet(c *fiber.Ctx) error { receiverIDString := c.Params("id") userID := c.Locals("user_id").(int64) receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) if err != nil { h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil) } // Get sender ID from the cashier var req CreateRefillReq if err := c.BodyParser(&req); err != nil { h.logger.Error("CreateRefillReq failed", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := h.validator.Validate(c, req) if !ok { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transfer, err := h.walletSvc.AddToWallet( c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{ Value: userID, Valid: true, }, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet directly by super-admin", req.Amount)) if !ok { return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil) } res := convertTransfer(transfer) return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) }