package v1 import ( "net/http" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/automation" "github.com/gochat/gochat/pkg/response" ) // BotRuleHandler handles bot rule API endpoints. // Reference: Chatwoot AutomationRulesController pattern adapted for AgentBot rules. type BotRuleHandler struct { svc *automation.BotRuleService } // NewBotRuleHandler creates a new BotRuleHandler. func NewBotRuleHandler(svc *automation.BotRuleService) *BotRuleHandler { return &BotRuleHandler{svc: svc} } // List retrieves all bot rules for an account. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules_configs func (h *BotRuleHandler) List(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } rules, svcErr := h.svc.ListByAccount(c.Request.Context(), accountID) if svcErr != nil { handleServiceError(c, svcErr) return } c.JSON(http.StatusOK, gin.H{ "bot_rules": rules, "meta": gin.H{"count": len(rules)}, }) } // ListByBot retrieves all bot rules for a specific AgentBot. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules func (h *BotRuleHandler) ListByBot(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } agentBotID, err := parseUintParam(c, "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id") return } rules, svcErr := h.svc.ListByAgentBot(c.Request.Context(), accountID, agentBotID) if svcErr != nil { handleServiceError(c, svcErr) return } c.JSON(http.StatusOK, gin.H{ "bot_rules": rules, "meta": gin.H{"count": len(rules)}, }) } // Get retrieves a single bot rule by ID. // GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:id func (h *BotRuleHandler) Get(c *gin.Context) { id, err := parseUintAnyParam(c, "rule_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } rule, svcErr := h.svc.GetByID(c.Request.Context(), id) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, rule) } // Create creates a new bot rule for an AgentBot. // POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules func (h *BotRuleHandler) Create(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } agentBotID, err := parseUintParam(c, "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id") return } var req struct { Name string `json:"name" binding:"required"` Description string `json:"description"` EventName automation.BotRuleEventType `json:"event_name" binding:"required"` Conditions automation.Conditions `json:"conditions"` Actions automation.Actions `json:"actions"` Status automation.BotRuleStatus `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } rule := &automation.BotRule{ AccountID: accountID, AgentBotID: agentBotID, Name: req.Name, Description: req.Description, EventName: req.EventName, Conditions: req.Conditions, Actions: req.Actions, Status: req.Status, } // Default to active if not specified if rule.Status == "" { rule.Status = automation.BotRuleStatusActive } if svcErr := h.svc.Create(c.Request.Context(), rule); svcErr != nil { handleServiceError(c, svcErr) return } response.Created(c, rule) } // Update updates an existing bot rule. // PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:id func (h *BotRuleHandler) Update(c *gin.Context) { id, err := parseUintAnyParam(c, "rule_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } existing, svcErr := h.svc.GetByID(c.Request.Context(), id) if svcErr != nil { handleServiceError(c, svcErr) return } var req struct { Name string `json:"name"` Description string `json:"description"` EventName automation.BotRuleEventType `json:"event_name"` Conditions automation.Conditions `json:"conditions"` Actions automation.Actions `json:"actions"` Status automation.BotRuleStatus `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } // Apply updates if req.Name != "" { existing.Name = req.Name } if req.Description != "" { existing.Description = req.Description } if req.EventName != "" { existing.EventName = req.EventName } if req.Conditions != nil { existing.Conditions = req.Conditions } if req.Actions != nil { existing.Actions = req.Actions } if req.Status != "" { existing.Status = req.Status } if svcErr := h.svc.Update(c.Request.Context(), existing); svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, existing) } // Delete soft-deletes a bot rule. // DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:id func (h *BotRuleHandler) Delete(c *gin.Context) { id, err := parseUintAnyParam(c, "rule_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if svcErr := h.svc.Delete(c.Request.Context(), id); svcErr != nil { handleServiceError(c, svcErr) return } response.NoContent(c) } // ToggleStatus toggles the active/inactive status of a bot rule. // PATCH /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:id/status func (h *BotRuleHandler) ToggleStatus(c *gin.Context) { id, err := parseUintAnyParam(c, "rule_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } var req struct { Status automation.BotRuleStatus `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } if svcErr := h.svc.ToggleStatus(c.Request.Context(), id, req.Status); svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, gin.H{"id": id, "status": req.Status}) } // Clone duplicates a bot rule. // POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/bot_rules/:id/clone func (h *BotRuleHandler) Clone(c *gin.Context) { id, err := parseUintAnyParam(c, "rule_id", "id", "agent_bot_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } cloned, svcErr := h.svc.Clone(c.Request.Context(), id) if svcErr != nil { handleServiceError(c, svcErr) return } response.Created(c, cloned) }