package handlers import ( "encoding/json" "net/http" "github.com/rede5/gohorsejobs/backend/internal/api/middleware" "github.com/rede5/gohorsejobs/backend/internal/services" ) type ChatHandlers struct { chatService *services.ChatService } func NewChatHandlers(s *services.ChatService) *ChatHandlers { return &ChatHandlers{chatService: s} } // ListConversations lists all conversations for the authenticated user // @Summary List Conversations // @Description List chat conversations for candidate or company // @Tags Chat // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {array} services.Conversation // @Router /api/v1/conversations [get] func (h *ChatHandlers) ListConversations(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID, _ := ctx.Value(middleware.ContextUserID).(string) tenantID, _ := ctx.Value(middleware.ContextTenantID).(string) roles := middleware.ExtractRoles(ctx.Value(middleware.ContextRoles)) if userID == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } isCandidate := false for _, r := range roles { if r == "candidate" || r == "CANDIDATE" { isCandidate = true break } } // Logic: If user has company (tenantID), prefer company view? // But a user might be both candidate and admin (unlikely in this domain model). // If tenantID is present, assume acting as Company. if tenantID != "" { isCandidate = false } else { isCandidate = true } convs, err := h.chatService.ListConversations(ctx, userID, tenantID, isCandidate) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(convs) } // ListMessages lists messages in a conversation // @Summary List Messages // @Description Get message history for a conversation // @Tags Chat // @Accept json // @Produce json // @Param id path string true "Conversation ID" // @Security BearerAuth // @Success 200 {array} services.Message // @Router /api/v1/conversations/{id}/messages [get] func (h *ChatHandlers) ListMessages(w http.ResponseWriter, r *http.Request) { conversationID := r.PathValue("id") if conversationID == "" { http.Error(w, "Conversation ID required", http.StatusBadRequest) return } msgs, err := h.chatService.ListMessages(r.Context(), conversationID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Mark "IsMine" userID, _ := r.Context().Value(middleware.ContextUserID).(string) for i := range msgs { if msgs[i].SenderID == userID { msgs[i].IsMine = true } } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(msgs) } // SendMessage sends a new message // @Summary Send Message // @Description Send a message to a conversation // @Tags Chat // @Accept json // @Produce json // @Param id path string true "Conversation ID" // @Param request body map[string]string true "Message Content" // @Security BearerAuth // @Success 200 {object} services.Message // @Router /api/v1/conversations/{id}/messages [post] func (h *ChatHandlers) SendMessage(w http.ResponseWriter, r *http.Request) { conversationID := r.PathValue("id") if conversationID == "" { http.Error(w, "Conversation ID required", http.StatusBadRequest) return } userID, _ := r.Context().Value(middleware.ContextUserID).(string) if userID == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req struct { Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid body", http.StatusBadRequest) return } if req.Content == "" { http.Error(w, "Content required", http.StatusBadRequest) return } msg, err := h.chatService.SendMessage(r.Context(), userID, conversationID, req.Content) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } msg.IsMine = true w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(msg) }