package v1 import ( "net/http" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/service" "github.com/gochat/gochat/pkg/response" ) // DraftMessageHandler handles draft message API endpoints. // Reference: Chatwoot app/controllers/api/v1/conversations/draft_messages_controller.rb type DraftMessageHandler struct { draftSvc *service.DraftMessageService } // NewDraftMessageHandler creates a new DraftMessageHandler. func NewDraftMessageHandler(draftSvc *service.DraftMessageService) *DraftMessageHandler { return &DraftMessageHandler{draftSvc: draftSvc} } // List retrieves all draft messages for a conversation (filtered by current user). // GET /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages func (h *DraftMessageHandler) List(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } conversationID, err := parseUintParam(c, "conversation_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid conversation_id") return } userID := getUserID(c) drafts, svcErr := h.draftSvc.List(c.Request.Context(), accountID, conversationID, userID) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, toInterfaceSlice(drafts)) } // Create creates a new draft message for a conversation. // POST /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages func (h *DraftMessageHandler) Create(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } conversationID, err := parseUintParam(c, "conversation_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid conversation_id") return } userID := getUserID(c) var req struct { Content string `json:"content" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } draft, svcErr := h.draftSvc.Create(c.Request.Context(), accountID, conversationID, userID, req.Content) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, draft) } // Get retrieves a specific draft message by ID. // GET /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:id func (h *DraftMessageHandler) Get(c *gin.Context) { draftID, err := parseUintParam(c, "draft_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } draft, svcErr := h.draftSvc.Get(c.Request.Context(), draftID) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, draft) } // Update updates an existing draft message. // PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:id func (h *DraftMessageHandler) Update(c *gin.Context) { draftID, err := parseUintParam(c, "draft_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } var req struct { Content string `json:"content" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } draft, svcErr := h.draftSvc.Update(c.Request.Context(), draftID, req.Content) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, draft) } // Delete deletes a draft message. // DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages/:id func (h *DraftMessageHandler) Delete(c *gin.Context) { draftID, err := parseUintParam(c, "draft_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if svcErr := h.draftSvc.Delete(c.Request.Context(), draftID); svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, nil) } // Search searches draft messages by content query for an account. // GET /api/v1/accounts/:account_id/draft_messages/search?q= func (h *DraftMessageHandler) Search(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } query := c.Query("q") if query == "" { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "search query 'q' is required") return } if !h.draftSvc.Ready() { response.AbortWithStatusError(c, http.StatusUnprocessableEntity, response.ErrInternal, "failed to search draft messages") return } drafts, svcErr := h.draftSvc.Search(c.Request.Context(), accountID, query) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, toInterfaceSlice(drafts)) } // Count returns the total number of draft messages for an account. // GET /api/v1/accounts/:account_id/draft_messages/count func (h *DraftMessageHandler) Count(c *gin.Context) { accountID, err := parseUintParam(c, "account_id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } if !h.draftSvc.Ready() { response.AbortWithStatusError(c, http.StatusUnprocessableEntity, response.ErrInternal, "failed to count draft messages") return } count, svcErr := h.draftSvc.Count(c.Request.Context(), accountID) if svcErr != nil { handleServiceError(c, svcErr) return } response.OK(c, gin.H{"count": count}) }