package promocode import ( "codeword/internal/models" "codeword/internal/repository" "codeword/internal/services" "codeword/internal/utils/middleware" "errors" "fmt" "github.com/gofiber/fiber/v2" "go.uber.org/zap" "strings" ) type Deps struct { Logger *zap.Logger PromoCodeService *services.PromoCodeService } type PromoCodeController struct { logger *zap.Logger promoCodeService *services.PromoCodeService } func NewPromoCodeController(deps Deps) *PromoCodeController { return &PromoCodeController{ logger: deps.Logger, promoCodeService: deps.PromoCodeService, } } func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error { userID := middleware.ExtractUserID(c) hlogger := middleware.ExtractLogger(c) var req models.PromoCode if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } if req.Codeword == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword is required"}) } req.ActivationLimit = req.ActivationCount createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req) if err != nil { p.logger.Error("Failed to create promocode", zap.Error(err)) if errors.Is(err, repository.ErrDuplicateCodeword) { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Duplicate Codeword"}) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } var keyType string var ctxFactor float64 var keyTargetType string var ctxTarget string var ctxAmount int64 if createdPromoCode.Bonus.Privilege.PrivilegeID != "" && createdPromoCode.Bonus.Discount.Factor != 0 && createdPromoCode.Bonus.Discount.Factor != 1 { keyType = "privilege,discount" keyTargetType = "privilege,service" ctxTarget = fmt.Sprintf("%s,%s", createdPromoCode.Bonus.Privilege.PrivilegeID, createdPromoCode.Bonus.Discount.Target) ctxAmount = int64(createdPromoCode.Bonus.Privilege.Amount) ctxFactor = createdPromoCode.Bonus.Discount.Factor } else if createdPromoCode.Bonus.Privilege.PrivilegeID != "" { keyType = "privilege" keyTargetType = "privilege" ctxTarget = createdPromoCode.Bonus.Privilege.PrivilegeID ctxAmount = int64(createdPromoCode.Bonus.Privilege.Amount) } else if createdPromoCode.Bonus.Discount.Factor != 0 && createdPromoCode.Bonus.Discount.Factor != 1 { keyType = "discount" keyTargetType = "service" ctxFactor = createdPromoCode.Bonus.Discount.Factor ctxTarget = createdPromoCode.Bonus.Discount.Target } hlogger.Emit(models.InfoPromocodeCreated{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: createdPromoCode.ID.String(), CtxUserID: userID, KeyType: keyType, CtxFactor: ctxFactor, KeyTargetType: keyTargetType, CtxTarget: ctxTarget, CtxAmount: ctxAmount, CtxCode: createdPromoCode.Codeword, }) return c.Status(fiber.StatusOK).JSON(createdPromoCode) } func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error { userID := middleware.ExtractUserID(c) hlogger := middleware.ExtractLogger(c) var req models.ReqEditPromoCode if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } if req.ID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "promocode ID is required"}) } req.ActivationLimit = req.ActivationCount editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &req) if err != nil { p.logger.Error("Failed to edit promocode", zap.Error(err)) if errors.Is(err, repository.ErrPromoCodeNotFound) { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } var keyType string var ctxFactor float64 var keyTargetType string var ctxTarget string var ctxAmount int64 if editedPromoCode.Bonus.Privilege.PrivilegeID != "" && editedPromoCode.Bonus.Discount.Factor != 0 && editedPromoCode.Bonus.Discount.Factor != 1 { keyType = "privilege,discount" keyTargetType = "privilege,service" ctxTarget = fmt.Sprintf("%s,%s", editedPromoCode.Bonus.Privilege.PrivilegeID, editedPromoCode.Bonus.Discount.Target) ctxAmount = int64(editedPromoCode.Bonus.Privilege.Amount) ctxFactor = editedPromoCode.Bonus.Discount.Factor } else if editedPromoCode.Bonus.Privilege.PrivilegeID != "" { keyType = "privilege" keyTargetType = "privilege" ctxTarget = editedPromoCode.Bonus.Privilege.PrivilegeID ctxAmount = int64(editedPromoCode.Bonus.Privilege.Amount) } else if editedPromoCode.Bonus.Discount.Factor != 0 && editedPromoCode.Bonus.Discount.Factor != 1 { keyType = "discount" keyTargetType = "service" ctxFactor = editedPromoCode.Bonus.Discount.Factor ctxTarget = editedPromoCode.Bonus.Discount.Target } hlogger.Emit(models.InfoPromocodeUpdated{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: editedPromoCode.ID.String(), CtxUserID: userID, KeyType: keyType, CtxFactor: ctxFactor, KeyTargetType: keyTargetType, CtxTarget: ctxTarget, CtxAmount: ctxAmount, CtxCode: editedPromoCode.Codeword, }) return c.Status(fiber.StatusOK).JSON(editedPromoCode) } func (p *PromoCodeController) GetList(c *fiber.Ctx) error { var req models.GetPromoCodesListReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } promoCodes, count, err := p.promoCodeService.GetPromoCodesList(c.Context(), &req) if err != nil { p.logger.Error("Failed to retrieve promocode list", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } return c.Status(fiber.StatusOK).JSON(models.GetPromoCodesListResp{ Count: count, Items: promoCodes, }) } func (p *PromoCodeController) Activate(c *fiber.Ctx) error { userID := middleware.ExtractUserID(c) hlogger := middleware.ExtractLogger(c) fmt.Println("SKER1", userID) if userID == "" { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"}) } var req models.ActivateReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } if req.Codeword == "" && req.FastLink == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword or fastlink is required"}) } fmt.Println("SKER2", req) promocode, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID) fmt.Println("SKER3", err) if err != nil { p.logger.Error("Failed to activate promocode", zap.Error(err)) switch { case errors.Is(err, repository.ErrPromoCodeNotFound): return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) case errors.Is(err, repository.ErrPromoCodeAlreadyActivated): return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "PromoCode already activated"}) case errors.Is(err, repository.ErrPromoCodeExpired): hlogger.Emit(models.InfoPromocodeDeadlined{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: promocode.ID.String(), }) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) case errors.Is(err, repository.ErrPromoCodeExhausted): hlogger.Emit(models.InfoPromocodeExhausted{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: promocode.ID.String(), }) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode exhausted"}) default: return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } } if req.Codeword != "" { hlogger.Emit(models.InfoPromocodeActivated{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: promocode.ID.String(), CtxUserID: userID, CtxCode: req.Codeword, }) } else if req.FastLink != "" { hlogger.Emit(models.InfoFastlinkActivated{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: promocode.ID.String(), CtxUserID: userID, CtxPromocodeID: req.FastLink, }) } return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: promocode.Greetings}) } func (p *PromoCodeController) Delete(c *fiber.Ctx) error { userID := middleware.ExtractUserID(c) hlogger := middleware.ExtractLogger(c) promoCodeID := c.Params("promocodeID") if promoCodeID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"}) } err := p.promoCodeService.DeletePromoCode(c.Context(), promoCodeID) if err != nil { p.logger.Error("Failed to delete promocode", zap.Error(err)) if errors.Is(err, repository.ErrPromoCodeNotFound) { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } hlogger.Emit(models.InfoPromocodeDeleted{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: promoCodeID, CtxUserID: userID, }) return c.SendStatus(fiber.StatusOK) } func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error { userID := middleware.ExtractUserID(c) hlogger := middleware.ExtractLogger(c) var req struct { PromoCodeID string `json:"id"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } if req.PromoCodeID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"}) } fastLink, err := p.promoCodeService.CreateFastLink(c.Context(), req.PromoCodeID) if err != nil { p.logger.Error("Failed to create fastlink", zap.Error(err)) if errors.Is(err, repository.ErrPromoCodeNotFound) { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } hlogger.Emit(models.InfoFastlinkCreated{ CtxUserIP: c.IP(), CtxUserPort: c.Port(), KeyDomain: strings.Join(c.Subdomains(), "/"), KeyPath: c.Path(), CtxID: fastLink, CtxPromocodeID: req.PromoCodeID, CtxUserID: userID, }) return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink}) } func (p *PromoCodeController) GetStats(c *fiber.Ctx) error { var req models.PromoStatReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) } if req.PromoCodeID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"}) } promoStats, err := p.promoCodeService.GetStats(c.Context(), req) if err != nil { p.logger.Error("Failed getting promo stats", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error: " + err.Error()}) } return c.Status(fiber.StatusOK).JSON(promoStats) }