package handlers import ( "log" "fmt" "sno/internal/models" "sno/internal/service" "strconv" "github.com/gofiber/fiber/v2" ) // AdminHandler handles HTTP requests for admin operations. type AdminHandler struct { adminService service.AdminService qrService service.QRService } // NewAdminHandler creates a new instance of an admin handler. func NewAdminHandler(adminSvc service.AdminService, qrSvc service.QRService) *AdminHandler { return &AdminHandler{ adminService: adminSvc, qrService: qrSvc, } } // GrantStars handles the request to manually grant stars to a user. // @Summary Grant stars to user // @Description Manually grants stars to a user (admin only) // @Tags admin // @Accept json // @Produce json // @Param request body models.GrantStarsRequest true "Grant stars request" // @Success 200 {object} object{success=bool,message=string} // @Failure 400 {object} object{success=bool,message=string} // @Failure 500 {object} object{success=bool,message=string} // @Router /api/admin/users/grant-stars [post] // @Security ApiKeyAuth func (h *AdminHandler) GrantStars(c *fiber.Ctx) error { var req models.GrantStarsRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Cannot parse JSON", }) } if err := h.adminService.GrantStars(c.Context(), req.UserID, req.Amount); err != nil { log.Printf("ERROR: Failed to grant stars: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(Response{ Success: false, Message: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(Response{ Success: true, Message: "Stars granted successfully", }) } // GenerateQRCodes handles the request to generate unique QR codes. // @Summary Generate QR codes // @Description Generates unique QR codes for rewards, quizzes, or shop items (admin/operator only) // @Tags admin // @Accept json // @Produce json // @Param request body models.GenerateQRCodesRequest true "QR codes generation request" // @Success 201 {object} object{success=bool,message=string,data=models.GenerateQRCodesResponse} // @Failure 400 {object} object{success=bool,message=string} // @Failure 500 {object} object{success=bool,message=string} // @Router /api/admin/qrcodes [post] // @Security ApiKeyAuth func (h *AdminHandler) GenerateQRCodes(c *fiber.Ctx) error { var req models.GenerateQRCodesRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Cannot parse JSON", }) } // Validate request if req.Type == "" { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Type cannot be empty", }) } if req.Value == "" { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Value cannot be empty", }) } if req.Count <= 0 || req.Count > 100 { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Count must be between 1 and 100", }) } // Validate type validTypes := map[string]bool{ "reward": true, "quiz": true, "shop": true, } if !validTypes[req.Type] { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Invalid type. Must be 'reward', 'quiz', or 'shop'", }) } tokens := make([]string, 0, req.Count) for i := 0; i < req.Count; i++ { token, err := h.qrService.GenerateUniqueToken(c.Context(), req.Type, req.Value) if err != nil { log.Printf("ERROR: Failed to generate QR token: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(Response{ Success: false, Message: "Failed to generate QR codes", }) } tokens = append(tokens, token) } return c.Status(fiber.StatusCreated).JSON(Response{ Success: true, Message: fmt.Sprintf("%d unique QR codes generated successfully", len(tokens)), Data: models.GenerateQRCodesResponse{ Tokens: tokens, }, }) } // CreateOperator handles the request to create a new operator // @Summary Create operator // @Description Creates a new operator account (admin only) // @Tags admin // @Accept json // @Produce json // @Param request body models.CreateOperatorRequest true "Create operator request" // @Success 201 {object} object{success=bool,message=string} // @Failure 400 {object} object{success=bool,message=string} // @Failure 500 {object} object{success=bool,message=string} // @Router /api/admin/operators [post] // @Security ApiKeyAuth func (h *AdminHandler) CreateOperator(c *fiber.Ctx) error { var req models.CreateOperatorRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Cannot parse JSON", }) } // Validate request if req.TelegramID == 0 { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Telegram ID cannot be empty", }) } if req.Name == "" { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Name cannot be empty", }) } if err := h.adminService.CreateOperator(c.Context(), req.TelegramID, req.Name); err != nil { log.Printf("ERROR: Failed to create operator: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(Response{ Success: false, Message: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(Response{ Success: true, Message: "Operator created successfully", }) } // DeleteOperator handles the request to delete an operator // @Summary Delete operator // @Description Deletes an operator account (admin only) // @Tags admin // @Accept json // @Produce json // @Param id path int true "Operator Telegram ID" // @Success 200 {object} object{success=bool,message=string} // @Failure 400 {object} object{success=bool,message=string} // @Failure 500 {object} object{success=bool,message=string} // @Router /api/admin/operators/{id} [delete] // @Security ApiKeyAuth func (h *AdminHandler) DeleteOperator(c *fiber.Ctx) error { telegramID, err := strconv.ParseInt(c.Params("id"), 10, 64) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(Response{ Success: false, Message: "Invalid Telegram ID", }) } if err := h.adminService.DeleteOperator(c.Context(), telegramID); err != nil { log.Printf("ERROR: Failed to delete operator: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(Response{ Success: false, Message: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(Response{ Success: true, Message: "Operator deleted successfully", }) } // GetAnalytics handles the request to get analytics data // @Summary Get analytics data // @Description Returns analytics data including user statistics, rewards, and quiz performance (admin/operator only) // @Tags admin // @Accept json // @Produce json // @Success 200 {object} object{success=bool,message=string,data=object} // @Failure 500 {object} object{success=bool,message=string} // @Router /api/admin/analytics [get] // @Security ApiKeyAuth func (h *AdminHandler) GetAnalytics(c *fiber.Ctx) error { analytics, err := h.adminService.GetAnalytics(c.Context()) if err != nil { log.Printf("ERROR: Failed to get analytics: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(Response{ Success: false, Message: "Failed to retrieve analytics", }) } return c.Status(fiber.StatusOK).JSON(Response{ Success: true, Message: "Analytics retrieved successfully", Data: analytics, }) }