package handlers import ( "log/slog" "strconv" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" ) // CreateShopBet godoc // @Summary Create bet at branch // @Description Create bet at branch // @Tags transaction // @Accept json // @Produce json // @Param createBet body domain.ShopBetReq true "create bet" // @Success 200 {object} TransactionRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/bet [post] func (h *Handler) CreateShopBet(c *fiber.Ctx) error { userID := c.Locals("user_id").(int64) role := c.Locals("role").(domain.Role) company_id := c.Locals("company_id").(domain.ValidInt64) var req domain.ShopBetReq if err := c.BodyParser(&req); err != nil { h.logger.Error("CreateBetReq failed to parse request", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := h.validator.Validate(c, req) if !ok { h.logger.Error("CreateBetReq failed v", "error", valErrs) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } shopBet, err := h.transactionSvc.CreateShopBet(c.Context(), userID, role, company_id, req) if err != nil { return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to cashout bet", err, nil) } res := domain.ConvertShopBet(shopBet) return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) } // CashoutBet godoc // @Summary Cashout bet at branch // @Description Cashout bet at branch // @Tags transaction // @Accept json // @Produce json // @Param createBet body domain.CashoutReq true "cashout bet" // @Success 200 {object} TransactionRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/bet/{id}/cashout [post] func (h *Handler) CashoutBet(c *fiber.Ctx) error { betIDStr := c.Params("id") betID, err := strconv.ParseInt(betIDStr, 10, 64) if err != nil { h.logger.Error("CashoutReq invalid bet id", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet id", err, nil) } userID := c.Locals("user_id").(int64) role := c.Locals("role").(domain.Role) var req domain.CashoutReq if err := c.BodyParser(&req); err != nil { h.logger.Error("CashoutReq failed to parse request", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } valErrs, ok := h.validator.Validate(c, req) if !ok { h.logger.Error("CashoutReq failed v", "error", valErrs) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req) if err != nil { return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to cashout bet", err, nil) } res := domain.ConvertShopTransaction(transaction) return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) } // DepositForCustomer godoc // @Summary Shop deposit into customer wallet // @Description Transfers money from branch wallet to customer wallet // @Tags transaction // @Accept json // @Produce json // @Param transferToWallet body domain.ShopDepositReq true "ShopDepositReq" // @Success 200 {object} TransferWalletRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/deposit [post] func (h *Handler) DepositForCustomer(c *fiber.Ctx) error { // Get sender ID from the cashier userID := c.Locals("user_id").(int64) role := c.Locals("role").(domain.Role) var req domain.ShopDepositReq 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) } deposit, err := h.transactionSvc.CreateShopDeposit(c.Context(), userID, role, req) if err != nil { return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to create shop deposit", err, nil) } res := domain.ConvertShopDeposit(deposit) return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) } // GetAllTransactions godoc // @Summary Gets all transactions // @Description Gets all the transactions // @Tags transaction // @Accept json // @Produce json // @Success 200 {array} TransactionRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/transaction [get] func (h *Handler) GetAllTransactions(c *fiber.Ctx) error { // Get user_id from middleware // userID := c.Locals("user_id").(int64) // role := c.Locals("role").(domain.Role) companyID := c.Locals("company_id").(domain.ValidInt64) branchID := c.Locals("branch_id").(domain.ValidInt64) searchQuery := c.Query("query") searchString := domain.ValidString{ Value: searchQuery, Valid: searchQuery != "", } createdBeforeQuery := c.Query("created_before") var createdBefore domain.ValidTime if createdBeforeQuery != "" { createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) if err != nil { h.logger.Error("invalid start_time format", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil) } createdBefore = domain.ValidTime{ Value: createdBeforeParsed, Valid: true, } } createdAfterQuery := c.Query("created_after") var createdAfter domain.ValidTime if createdAfterQuery != "" { createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) if err != nil { h.logger.Error("invalid start_time format", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil) } createdAfter = domain.ValidTime{ Value: createdAfterParsed, Valid: true, } } // Check user role and fetch transactions accordingly transactions, err := h.transactionSvc.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{ CompanyID: companyID, BranchID: branchID, Query: searchString, CreatedBefore: createdBefore, CreatedAfter: createdAfter, }) if err != nil { h.logger.Error("Failed to get transactions", "error", err) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil) } res := make([]domain.ShopTransactionRes, len(transactions)) for i, transaction := range transactions { res[i] = domain.ConvertShopTransactionDetail(transaction) } return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil) } // GetTransactionByID godoc // @Summary Gets transaction by id // @Description Gets a single transaction by id // @Tags transaction // @Accept json // @Produce json // @Param id path int true "Transaction ID" // @Success 200 {object} TransactionRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/transaction/{id} [get] func (h *Handler) GetTransactionByID(c *fiber.Ctx) error { transactionID := c.Params("id") id, err := strconv.ParseInt(transactionID, 10, 64) if err != nil { h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err) return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID") } transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id) if err != nil { h.logger.Error("Failed to get transaction by ID", "transactionID", id, "error", err) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve transaction") } res := domain.ConvertShopTransactionDetail(transaction) return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil) } type UpdateTransactionVerifiedReq struct { Verified bool `json:"verified" example:"true"` } // UpdateTransactionVerified godoc // @Summary Updates the verified field of a transaction // @Description Updates the verified status of a transaction // @Tags transaction // @Accept json // @Produce json // @Param id path int true "Transaction ID" // @Param updateVerified body UpdateTransactionVerifiedReq true "Updates Transaction Verification" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /shop/transaction/{id} [put] func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error { transactionID := c.Params("id") userID := c.Locals("user_id").(int64) companyID := c.Locals("company_id").(domain.ValidInt64) role := c.Locals("role").(domain.Role) id, err := strconv.ParseInt(transactionID, 10, 64) if err != nil { h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err) return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID") } var req UpdateTransactionVerifiedReq if err := c.BodyParser(&req); err != nil { h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } h.logger.Info("Update Transaction Verified", slog.Bool("verified", req.Verified)) if valErrs, ok := h.validator.Validate(c, req); !ok { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id) if role != domain.RoleSuperAdmin { if !companyID.Valid || companyID.Value != transaction.CompanyID { h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) } } user, err := h.userSvc.GetUserById(c.Context(), userID) if err != nil { h.logger.Error("Invalid user ID", "userID", userID, "error", err) return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID") } err = h.transactionSvc.UpdateShopTransactionVerified(c.Context(), id, req.Verified, userID, user.FirstName+" "+user.LastName) if err != nil { h.logger.Error("Failed to update transaction verification", "transactionID", id, "error", err) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transaction verification") } return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil) }