package v1 import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/service" applogger "github.com/gochat/gochat/pkg/logger" ) // WebWidgetOfflineHandler handles admin CRUD for widget offline messages. // These endpoints require agent JWT authentication and account scope. // Reference: Chatwoot admin view of pending offline messages from web widget visitors. type WebWidgetOfflineHandler struct { widgetSvc *service.WidgetService inboxSvc *service.InboxService } // NewWebWidgetOfflineHandler creates a new admin widget offline message handler. func NewWebWidgetOfflineHandler(widgetSvc *service.WidgetService, inboxSvc *service.InboxService) *WebWidgetOfflineHandler { return &WebWidgetOfflineHandler{widgetSvc: widgetSvc, inboxSvc: inboxSvc} } // ListOfflineMessages retrieves paginated offline messages for an account. // GET /api/v1/accounts/:account_id/web_widgets/offline_messages func (h *WebWidgetOfflineHandler) ListOfflineMessages(c *gin.Context) { accountIDStr := c.Param("id") accountID, err := strconv.ParseUint(accountIDStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account_id"}) return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "25")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 25 } msgs, total, err := h.widgetSvc.ListOfflineMessagesByAccount(c.Request.Context(), uint(accountID), page, pageSize) if err != nil { applogger.L().Errorf("Failed to list offline messages for account=%d: %v", accountID, err) c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "offline_messages": msgs, "meta": gin.H{ "page": page, "page_size": pageSize, "total_count": total, }, }) } // ListOfflineMessagesByInbox retrieves offline messages for a specific inbox. // GET /api/v1/accounts/:account_id/inboxes/:inbox_id/web_widget/offline_messages func (h *WebWidgetOfflineHandler) ListOfflineMessagesByInbox(c *gin.Context) { inboxIDStr := c.Param("inbox_id") inboxID, err := strconv.ParseUint(inboxIDStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox_id"}) return } accountIDStr := c.Param("id") accountID, err := strconv.ParseUint(accountIDStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account_id"}) return } // Verify inbox belongs to account inbox, err := h.inboxSvc.GetByAccountAndID(c.Request.Context(), uint(accountID), uint(inboxID)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "inbox not found"}) return } if inbox.ChannelType != "web_widget" { c.JSON(http.StatusBadRequest, gin.H{"error": "offline messages only supported for web_widget channel"}) return } msgs, err := h.widgetSvc.GetOfflineMessages(c.Request.Context(), uint(inboxID)) if err != nil { applogger.L().Errorf("Failed to get offline messages for inbox=%d: %v", inboxID, err) c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"offline_messages": msgs}) } // DismissOfflineMessage marks an offline message as dismissed by an agent. // PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/dismiss func (h *WebWidgetOfflineHandler) DismissOfflineMessage(c *gin.Context) { offlineMsgIDStr := c.Param("offline_message_id") offlineMsgID, err := strconv.ParseUint(offlineMsgIDStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid offline_message_id"}) return } err = h.widgetSvc.DismissOfflineMessage(c.Request.Context(), uint(offlineMsgID)) if err != nil { applogger.L().Errorf("Failed to dismiss offline message=%d: %v", offlineMsgID, err) c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "offline message dismissed"}) } // ConvertOfflineMessage converts an offline message into a conversation. // PUT /api/v1/accounts/:account_id/web_widgets/offline_messages/:offline_message_id/convert func (h *WebWidgetOfflineHandler) ConvertOfflineMessage(c *gin.Context) { offlineMsgIDStr := c.Param("offline_message_id") offlineMsgID, err := strconv.ParseUint(offlineMsgIDStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid offline_message_id"}) return } conversation, message, err := h.widgetSvc.ConvertOfflineMessageToConversation(c.Request.Context(), uint(offlineMsgID)) if err != nil { applogger.L().Errorf("Failed to convert offline message=%d: %v", offlineMsgID, err) c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "conversation": conversation, "message": message, }) }