package handlers import ( "log/slog" "strconv" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "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 CreateBetReq struct { Outcomes []int64 `json:"outcomes"` Amount float32 `json:"amount" example:"100.0"` TotalOdds float32 `json:"total_odds" example:"4.22"` Status domain.BetStatus `json:"status" example:"1"` FullName string `json:"full_name" example:"John"` PhoneNumber string `json:"phone_number" example:"1234567890"` IsShopBet bool `json:"is_shop_bet" example:"false"` } type BetRes struct { ID int64 `json:"id" example:"1"` Outcomes []domain.Outcome `json:"outcomes"` Amount float32 `json:"amount" example:"100.0"` TotalOdds float32 `json:"total_odds" example:"4.22"` Status domain.BetStatus `json:"status" example:"1"` FullName string `json:"full_name" example:"John"` PhoneNumber string `json:"phone_number" example:"1234567890"` BranchID int64 `json:"branch_id" example:"2"` UserID int64 `json:"user_id" example:"2"` IsShopBet bool `json:"is_shop_bet" example:"false"` } func convertBet(bet domain.Bet) BetRes { return BetRes{ ID: bet.ID, Outcomes: bet.Outcomes, Amount: bet.Amount.Float64(), TotalOdds: bet.TotalOdds, Status: bet.Status, FullName: bet.FullName, PhoneNumber: bet.PhoneNumber, BranchID: bet.BranchID.Value, UserID: bet.UserID.Value, } } // CreateBet godoc // @Summary Create a bet // @Description Creates a bet // @Tags bet // @Accept json // @Produce json // @Param createBet body CreateBetReq true "Creates bet" // @Success 200 {object} BetRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /bet [post] func CreateBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { // TODO if user is customer, get id from the token then get the wallet id from there // TODO: If user is a cashier, check the token, and find the role and get the branch id from there. Reduce amount from the branch wallet var isShopBet bool = true var branchID int64 = 1 var userID int64 var req CreateBetReq if err := c.BodyParser(&req); err != nil { logger.Error("CreateBetReq failed", "error", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid request", }) } valErrs, ok := validator.Validate(c, req) if !ok { response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return nil } // TODO Validate Outcomes Here and make sure they didn't expire bet, err := betSvc.CreateBet(c.Context(), domain.CreateBet{ Outcomes: req.Outcomes, Amount: domain.Currency(req.Amount), TotalOdds: req.TotalOdds, Status: req.Status, FullName: req.FullName, PhoneNumber: req.PhoneNumber, BranchID: domain.ValidInt64{ Value: branchID, Valid: isShopBet, }, UserID: domain.ValidInt64{ Value: userID, Valid: !isShopBet, }, IsShopBet: req.IsShopBet, }) if err != nil { logger.Error("CreateBetReq failed", "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil) } res := convertBet(bet) return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil) } } // GetAllBet godoc // @Summary Gets all bets // @Description Gets all the bets // @Tags bet // @Accept json // @Produce json // @Success 200 {array} BetRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /bet [get] func GetAllBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { bets, err := betSvc.GetAllBets(c.Context()) if err != nil { logger.Error("Failed to get bets", "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bets", err, nil) } var res []BetRes = make([]BetRes, len(bets)) for _, bet := range bets { res = append(res, convertBet(bet)) } return response.WriteJSON(c, fiber.StatusOK, "All Bets Retrieved", res, nil) } } // GetBetByID godoc // @Summary Gets bet by id // @Description Gets a single bet by id // @Tags bet // @Accept json // @Produce json // @Param id path int true "Bet ID" // @Success 200 {object} BetRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /bet/{id} [get] func GetBetByID(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { betID := c.Params("id") id, err := strconv.ParseInt(betID, 10, 64) if err != nil { logger.Error("Invalid bet ID", "betID", betID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet ID", err, nil) } bet, err := betSvc.GetBetByID(c.Context(), id) if err != nil { logger.Error("Failed to get bet by ID", "betID", id, "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil) } res := convertBet(bet) return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil) } } type UpdateCashOutReq struct { CashedOut bool } // UpdateCashOut godoc // @Summary Updates the cashed out field // @Description Updates the cashed out field // @Tags bet // @Accept json // @Produce json // @Param id path int true "Bet ID" // @Param updateCashOut body UpdateCashOutReq true "Updates Cashed Out" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /bet/{id} [patch] func UpdateCashOut(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { betID := c.Params("id") id, err := strconv.ParseInt(betID, 10, 64) if err != nil { logger.Error("Invalid bet ID", "betID", betID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet ID", err, nil) } var req UpdateCashOutReq if err := c.BodyParser(&req); err != nil { logger.Error("UpdateCashOutReq failed", "error", err) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid request", }) } valErrs, ok := validator.Validate(c, req) if !ok { response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return nil } err = betSvc.UpdateCashOut(c.Context(), id, req.CashedOut) if err != nil { logger.Error("Failed to update cash out bet", "betID", id, "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update cash out bet", err, nil) } return response.WriteJSON(c, fiber.StatusOK, "Bet updated successfully", nil, nil) } } // DeleteBet godoc // @Summary Deletes bet by id // @Description Deletes bet by id // @Tags bet // @Accept json // @Produce json // @Param id path int true "Bet ID" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /bet/{id} [delete] func DeleteBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler { return func(c *fiber.Ctx) error { betID := c.Params("id") id, err := strconv.ParseInt(betID, 10, 64) if err != nil { logger.Error("Invalid bet ID", "betID", betID, "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet ID", err, nil) } err = betSvc.DeleteBet(c.Context(), id) if err != nil { logger.Error("Failed to delete by ID", "betID", id, "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to delete bet", err, nil) } return response.WriteJSON(c, fiber.StatusOK, "Bet removed successfully", nil, nil) } }