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 WalletRes struct { ID int64 `json:"id" example:"1"` Balance float32 `json:"amount" example:"100.0"` IsWithdraw bool `json:"is_withdraw" example:"true"` IsBettable bool `json:"is_bettable" example:"true"` IsTransferable bool `json:"is_transferable" example:"true"` IsActive bool `json:"is_active" example:"true"` UserID int64 `json:"user_id" example:"1"` UpdatedAt time.Time `json:"updated_at"` CreatedAt time.Time `json:"created_at"` } func convertWallet(wallet domain.Wallet) WalletRes { return WalletRes{ ID: wallet.ID, Balance: wallet.Balance.Float32(), IsWithdraw: wallet.IsWithdraw, IsBettable: wallet.IsBettable, IsTransferable: wallet.IsTransferable, IsActive: wallet.IsActive, UserID: wallet.UserID, UpdatedAt: wallet.UpdatedAt, CreatedAt: wallet.CreatedAt, } } type CustomerWalletRes struct { ID int64 `json:"id" example:"1"` RegularID int64 `json:"regular_id" example:"1"` RegularBalance float32 `json:"regular_balance" example:"100.0"` StaticID int64 `json:"static_id" example:"1"` StaticBalance float32 `json:"static_balance" example:"100.0"` CustomerID int64 `json:"customer_id" example:"1"` RegularIsActive bool `json:"regular_is_active" example:"true"` StaticIsActive bool `json:"static_is_active" example:"true"` RegularUpdatedAt time.Time `json:"regular_updated_at"` StaticUpdatedAt time.Time `json:"static_updated_at"` CreatedAt time.Time `json:"created_at"` FirstName string `json:"first_name" example:"John"` LastName string `json:"last_name" example:"Smith"` PhoneNumber string `json:"phone_number" example:"0911111111"` } func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes { return CustomerWalletRes{ ID: wallet.ID, RegularID: wallet.RegularID, RegularBalance: wallet.RegularBalance.Float32(), StaticID: wallet.StaticID, StaticBalance: wallet.StaticBalance.Float32(), CustomerID: wallet.CustomerID, RegularIsActive: wallet.RegularIsActive, StaticIsActive: wallet.StaticIsActive, RegularUpdatedAt: wallet.RegularUpdatedAt, StaticUpdatedAt: wallet.StaticUpdatedAt, CreatedAt: wallet.CreatedAt, FirstName: wallet.FirstName, LastName: wallet.LastName, PhoneNumber: wallet.PhoneNumber, } } type BranchWalletRes struct { ID int64 `json:"id" example:"1"` Balance float32 `json:"balance" example:"100.0"` IsActive bool `json:"is_active" example:"true"` Name string `json:"name" example:"true"` Location string `json:"location" example:"somewhere"` BranchManagerID int64 `json:"branch_manager_id" example:"1"` CompanyID int64 `json:"company_id" example:"1"` IsSelfOwned bool `json:"is_self_owned" example:"false"` UpdatedAt time.Time `json:"updated_at"` CreatedAt time.Time `json:"created_at"` } // GetWalletByID godoc // @Summary Get wallet by ID // @Description Retrieve wallet details by wallet ID // @Tags wallet // @Accept json // @Produce json // @Param id path int true "Wallet ID" // @Success 200 {object} WalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/wallet/{id} [get] func (h *Handler) GetWalletByID(c *fiber.Ctx) error { walletID := c.Params("id") id, err := strconv.ParseInt(walletID, 10, 64) if err != nil { h.mongoLoggerSvc.Error("Invalid wallet ID", zap.String("walletID", walletID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") } wallet, err := h.walletSvc.GetWalletByID(c.Context(), id) if err != nil { h.mongoLoggerSvc.Error("Failed to get wallet by ID", zap.Int64("walletID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet") } res := convertWallet(wallet) return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) } // GetAllWallets godoc // @Summary Get all wallets // @Description Retrieve all wallets // @Tags wallet // @Accept json // @Produce json // @Success 200 {array} WalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/wallet [get] func (h *Handler) GetAllWallets(c *fiber.Ctx) error { wallets, err := h.walletSvc.GetAllWallets(c.Context()) if err != nil { h.mongoLoggerSvc.Error("Failed to get wallets", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var res []WalletRes = make([]WalletRes, 0, len(wallets)) for _, wallet := range wallets { res = append(res, convertWallet(wallet)) } return response.WriteJSON(c, fiber.StatusOK, "All wallets retrieved successfully", res, nil) } // GetAllBranchWallets godoc // @Summary Get all branch wallets // @Description Retrieve all branch wallets // @Tags wallet // @Accept json // @Produce json // @Success 200 {array} WalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branchWallet [get] func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error { wallets, err := h.walletSvc.GetAllBranchWallets(c.Context()) if err != nil { h.mongoLoggerSvc.Error("Failed to get wallets", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallets") } var res []BranchWalletRes = make([]BranchWalletRes, 0, len(wallets)) for _, wallet := range wallets { res = append(res, BranchWalletRes{ ID: wallet.ID, Balance: wallet.Balance.Float32(), IsActive: wallet.IsActive, Name: wallet.Name, Location: wallet.Location, BranchManagerID: wallet.BranchManagerID, CompanyID: wallet.CompanyID, IsSelfOwned: wallet.IsSelfOwned, UpdatedAt: wallet.UpdatedAt, CreatedAt: wallet.CreatedAt, }) } return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil) } // GetAllCustomerWallets godoc // @Summary Get all customer wallets // @Description Retrieve all customer wallets // @Tags wallet // @Accept json // @Produce json // @Success 200 {array} CustomerWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/customerWallet [get] func (h *Handler) GetAllCustomerWallets(c *fiber.Ctx) error { wallets, err := h.walletSvc.GetAllCustomerWallet(c.Context()) if err != nil { h.mongoLoggerSvc.Error("Failed to get customer wallets", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallets") } var res []CustomerWalletRes = make([]CustomerWalletRes, 0, len(wallets)) for _, wallet := range wallets { res = append(res, ConvertCustomerWallet(wallet)) } return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil) } type UpdateWalletActiveReq struct { IsActive bool `json:"is_active" validate:"required" example:"true"` } // UpdateWalletActive godoc // @Summary Activate and Deactivate Wallet // @Description Can activate and deactivate wallet // @Tags wallet // @Accept json // @Produce json // @Param id path int true "Wallet ID" // @Param updateCashOut body UpdateWalletActiveReq true "Update Wallet Active" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/wallet/{id} [patch] func (h *Handler) UpdateWalletActive(c *fiber.Ctx) error { walletID := c.Params("id") id, err := strconv.ParseInt(walletID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid wallet ID", zap.String("walletID", walletID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") } var req UpdateWalletActiveReq if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse UpdateWalletActive request", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") } if valErrs, ok := h.validator.Validate(c, req); !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } h.mongoLoggerSvc.Info("Failed to validate UpdateWalletActiveReq", zap.String("errMsg", errMsg), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, errMsg) } err = h.walletSvc.UpdateWalletActive(c.Context(), id, req.IsActive) if err != nil { h.mongoLoggerSvc.Error("Failed to update wallet active status", zap.Int64("walletID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update wallet") } return response.WriteJSON(c, fiber.StatusOK, "Wallet successfully updated", nil, nil) } // GetCustomerWallet godoc // @Summary Get customer wallet // @Description Retrieve customer wallet details // @Tags wallet // @Accept json // @Produce json // @Param company_id header int true "Company ID" // @Security Bearer // @Success 200 {object} CustomerWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/{tenant_slug}/user/wallet [get] func (h *Handler) GetCustomerWallet(c *fiber.Ctx) error { userID, ok := c.Locals("user_id").(int64) if !ok || userID == 0 { h.mongoLoggerSvc.Info("Invalid user ID in context", zap.Int("status_code", fiber.StatusInternalServerError), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Invalid user id in context") } role, ok := c.Locals("role").(domain.Role) if !ok { h.mongoLoggerSvc.Error("Invalid role in context", zap.Int64("userID", userID), zap.Int("status_code", fiber.StatusInternalServerError), zap.String("role", string(role)), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Invalid role") } if role != domain.RoleCustomer { h.mongoLoggerSvc.Error("Unauthorized access", zap.Int64("userID", userID), zap.Int("status_code", fiber.StatusForbidden), zap.String("role", string(role)), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") } // companyID, err := strconv.ParseInt(c.Get("company_id"), 10, 64) // if err != nil { // h.logger.Error("Invalid company_id header", "value", c.Get("company_id"), "error", err) // return fiber.NewError(fiber.StatusBadRequest, "Invalid company_id") // } // h.logger.Info("Fetching customer wallet", "userID", userID) wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID) if err != nil { h.mongoLoggerSvc.Error("Failed to get customer wallet", zap.Int64("userID", userID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet") } res := ConvertCustomerWallet(wallet) return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) } // GetWalletForCashier godoc // @Summary Get wallet for cashier // @Description Get wallet for cashier // @Tags cashier // @Accept json // @Produce json // @Param id path int true "User ID" // @Success 200 {object} UserProfileRes // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/cashierWallet [get] func (h *Handler) GetWalletForCashier(c *fiber.Ctx) error { cashierID, ok := c.Locals("user_id").(int64) if !ok || cashierID == 0 { h.mongoLoggerSvc.Error("Invalid cashier ID in context", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusUnauthorized, "Invalid cashier id") } role, ok := c.Locals("role").(domain.Role) if !ok || role != domain.RoleCashier { h.mongoLoggerSvc.Error("Unauthorized access", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusForbidden), zap.String("role", string(role)), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") } branchID, ok := c.Locals("branch_id").(domain.ValidInt64) if !ok || !branchID.Valid { h.mongoLoggerSvc.Info("Invalid branch ID in context", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } branch, err := h.branchSvc.GetBranchByID(c.Context(), branchID.Value) if err != nil { h.mongoLoggerSvc.Error("Failed to get branch by ID", zap.Int64("branchID", branchID.Value), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } wallet, err := h.walletSvc.GetWalletByID(c.Context(), branch.WalletID) if err != nil { h.mongoLoggerSvc.Error("Failed to get wallet for cashier", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := WalletRes{ ID: wallet.ID, Balance: wallet.Balance.Float32(), IsWithdraw: wallet.IsWithdraw, IsBettable: wallet.IsBettable, IsTransferable: wallet.IsTransferable, IsActive: wallet.IsActive, UserID: wallet.UserID, UpdatedAt: wallet.UpdatedAt, CreatedAt: wallet.CreatedAt, } return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) }