package handlers import ( "log/slog" "strconv" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" "github.com/gofiber/fiber/v2" ) type TransferWalletRes 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"` } 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(transfer domain.Transfer) TransferWalletRes { var senderWalletID *int64 if transfer.SenderWalletID.Valid { senderWalletID = &transfer.SenderWalletID.Value } var cashierID *int64 if transfer.CashierID.Valid { cashierID = &transfer.CashierID.Value } return TransferWalletRes{ ID: transfer.ID, Amount: transfer.Amount.Float64(), Verified: transfer.Verified, Type: string(transfer.Type), PaymentMethod: string(transfer.PaymentMethod), ReceiverWalletID: transfer.ReceiverWalletID, SenderWalletID: senderWalletID, CashierID: cashierID, CreatedAt: transfer.CreatedAt, UpdatedAt: transfer.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 GetTransfersByWallet(logger *slog.Logger, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { walletID := c.Params("id") id, err := strconv.ParseInt(walletID, 10, 64) if err != nil { logger.Error("Invalid wallet ID", "walletID", walletID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil) } transfers, err := walletSvc.GetTransfersByWallet(c.Context(), int64(id)) if err != nil { 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, convertTransfer(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 TransferToWallet(logger *slog.Logger, walletSvc *wallet.Service, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { receiverIDString := c.Params("id") receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) if err != nil { 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 := string(c.Locals("role").(domain.Role)) var senderID int64 if role == string(domain.RoleCustomer) { logger.Error("Unauthorized access", "userID", userID, "role", role) return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) } else if role == string(domain.RoleBranchManager) || role == string(domain.RoleAdmin) || role == string(domain.RoleSuperAdmin) { // TODO Add a way for admins to reference branch wallet senderID = 0 logger.Error("Will", "userID", userID, "role", role) return response.WriteJSON(c, fiber.StatusBadRequest, "Unauthorized access", nil, nil) } else { cashierBranch, err := branchSvc.GetBranchByCashier(c.Context(), userID) if err != nil { 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 } var req CreateTransferReq if err := c.BodyParser(&req); err != nil { logger.Error("CreateTransferReq failed", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := validator.Validate(c, req) if !ok { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transfer, err := walletSvc.TransferToWallet(c.Context(), senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod), domain.ValidInt64{Value: userID, Valid: true}) if !ok { 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 RefillWallet(logger *slog.Logger, walletSvc *wallet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { receiverIDString := c.Params("id") receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) if err != nil { 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 := string(c.Locals("role").(domain.Role)) if role != string(domain.RoleSuperAdmin) { logger.Error("Unauthorized access", "userID", userID, "role", role) return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) } var req CreateRefillReq if err := c.BodyParser(&req); err != nil { logger.Error("CreateRefillReq failed", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := validator.Validate(c, req) if !ok { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transfer, err := walletSvc.RefillWallet(c.Context(), domain.CreateTransfer{ Amount: domain.ToCurrency(req.Amount), PaymentMethod: domain.TRANSFER_BANK, ReceiverWalletID: receiverID, CashierID: domain.ValidInt64{ Value: userID, Valid: true, }, Type: domain.TransferType("deposit"), }) 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) } }