package handlers import ( "net/http" "strconv" "time" "game-admin/internal/config" "game-admin/internal/models" "github.com/gin-gonic/gin" "github.com/uptrace/bun" ) type GameHandler struct { db *bun.DB cfg *config.Config } func NewGameHandler(db *bun.DB, cfg *config.Config) *GameHandler { return &GameHandler{db: db, cfg: cfg} } func (h *GameHandler) List(c *gin.Context) { var games []models.Game query := h.db.NewSelect().Model(&games).OrderExpr("id DESC") if s := c.Query("search"); s != "" { like := "%" + s + "%" query = query.Where("g.name LIKE ? OR g.slug LIKE ?", like, like) } err := query.Scan(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if err := h.attachModulesToGames(c, games); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, games) } func (h *GameHandler) GetByID(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid game id"}) return } game := new(models.Game) err = h.db.NewSelect(). Model(game). Where("g.id = ?", id). Scan(c) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Game not found"}) return } modulesByGame, err := h.loadModulesByGameIDs(c, []int64{game.ID}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } game.Modules = modulesByGame[game.ID] c.JSON(http.StatusOK, game) } func (h *GameHandler) Create(c *gin.Context) { var req models.CreateGameRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } game := &models.Game{ Name: req.Name, Slug: req.Slug, Description: req.Description, ImageURL: req.ImageURL, IsActive: true, CreatedAt: time.Now(), UpdatedAt: time.Now(), } _, err := h.db.NewInsert().Model(game).Exec(c) if err != nil { c.JSON(http.StatusConflict, gin.H{"error": "Game slug already exists"}) return } c.JSON(http.StatusCreated, game) } func (h *GameHandler) Update(c *gin.Context) { id, _ := strconv.ParseInt(c.Param("id"), 10, 64) var req models.CreateGameRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } _, err := h.db.NewUpdate().Model((*models.Game)(nil)). Set("name = ?", req.Name). Set("slug = ?", req.Slug). Set("description = ?", req.Description). Set("image_url = ?", req.ImageURL). Set("updated_at = ?", time.Now()). Where("id = ?", id). Exec(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Game updated"}) } func (h *GameHandler) Delete(c *gin.Context) { id, _ := strconv.ParseInt(c.Param("id"), 10, 64) _, err := h.db.NewDelete().Model((*models.Game)(nil)).Where("id = ?", id).Exec(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Game deleted"}) } // ─── User-Game connections ────────────────────────────────── func (h *GameHandler) ConnectGame(c *gin.Context) { userID, _ := strconv.ParseInt(c.Param("id"), 10, 64) var req models.ConnectGameRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } ug := &models.UserGame{ UserID: userID, GameID: req.GameID, Balance: 0, CreatedAt: time.Now(), UpdatedAt: time.Now(), } _, err := h.db.NewInsert().Model(ug).Exec(c) if err != nil { c.JSON(http.StatusConflict, gin.H{"error": "Game already connected"}) return } c.JSON(http.StatusCreated, ug) } func (h *GameHandler) DisconnectGame(c *gin.Context) { userID, _ := strconv.ParseInt(c.Param("id"), 10, 64) gameID, _ := strconv.ParseInt(c.Param("game_id"), 10, 64) _, err := h.db.NewDelete().Model((*models.UserGame)(nil)). Where("user_id = ? AND game_id = ?", userID, gameID). Exec(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Game disconnected"}) } func (h *GameHandler) UserGames(c *gin.Context) { userID, _ := strconv.ParseInt(c.Param("id"), 10, 64) var userGames []models.UserGame err := h.db.NewSelect().Model(&userGames). Relation("Game"). Where("ug.user_id = ?", userID). Scan(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, userGames) } func (h *GameHandler) TopUp(c *gin.Context) { ugID, _ := strconv.ParseInt(c.Param("ug_id"), 10, 64) adminID, _ := c.Get("user_id") var req models.TopUpRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tx, err := h.db.BeginTx(c, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer tx.Rollback() // Update balance _, err = tx.NewUpdate().Model((*models.UserGame)(nil)). Set("balance = balance + ?", req.Amount). Set("updated_at = ?", time.Now()). Where("id = ?", ugID). Exec(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Log transaction txn := &models.BalanceTransaction{ UserGameID: ugID, Amount: req.Amount, Type: "topup", Comment: req.Comment, AdminID: adminID.(int64), CreatedAt: time.Now(), } _, err = tx.NewInsert().Model(txn).Exec(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if err := tx.Commit(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Balance topped up", "transaction": txn}) } func (h *GameHandler) Transactions(c *gin.Context) { ugID, _ := strconv.ParseInt(c.Param("ug_id"), 10, 64) var txns []models.BalanceTransaction err := h.db.NewSelect().Model(&txns). Where("user_game_id = ?", ugID). OrderExpr("id DESC"). Scan(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, txns) }