package handlers import ( "net/http" "github.com/breakpilot/ai-compliance-sdk/internal/portfolio" "github.com/breakpilot/ai-compliance-sdk/internal/rbac" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // ============================================================================ // Portfolio Items // ============================================================================ // AddItem adds an item to a portfolio // POST /sdk/v1/portfolios/:id/items func (h *PortfolioHandlers) AddItem(c *gin.Context) { portfolioID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"}) return } var req portfolio.AddItemRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } userID := rbac.GetUserID(c) item := &portfolio.PortfolioItem{ PortfolioID: portfolioID, ItemType: req.ItemType, ItemID: req.ItemID, Tags: req.Tags, Notes: req.Notes, AddedBy: userID, } if err := h.store.AddItem(c.Request.Context(), item); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"item": item}) } // ListItems lists items in a portfolio // GET /sdk/v1/portfolios/:id/items func (h *PortfolioHandlers) ListItems(c *gin.Context) { portfolioID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"}) return } var itemType *portfolio.ItemType if t := c.Query("type"); t != "" { it := portfolio.ItemType(t) itemType = &it } items, err := h.store.ListItems(c.Request.Context(), portfolioID, itemType) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "items": items, "total": len(items), }) } // BulkAddItems adds multiple items to a portfolio // POST /sdk/v1/portfolios/:id/items/bulk func (h *PortfolioHandlers) BulkAddItems(c *gin.Context) { portfolioID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"}) return } var req portfolio.BulkAddItemsRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } userID := rbac.GetUserID(c) items := make([]portfolio.PortfolioItem, len(req.Items)) for i, r := range req.Items { items[i] = portfolio.PortfolioItem{ ItemType: r.ItemType, ItemID: r.ItemID, Tags: r.Tags, Notes: r.Notes, } } result, err := h.store.BulkAddItems(c.Request.Context(), portfolioID, items, userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, result) } // RemoveItem removes an item from a portfolio // DELETE /sdk/v1/portfolios/:id/items/:itemId func (h *PortfolioHandlers) RemoveItem(c *gin.Context) { itemID, err := uuid.Parse(c.Param("itemId")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid item ID"}) return } if err := h.store.RemoveItem(c.Request.Context(), itemID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "item removed"}) } // ReorderItems updates the order of items // PUT /sdk/v1/portfolios/:id/items/order func (h *PortfolioHandlers) ReorderItems(c *gin.Context) { portfolioID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"}) return } var req struct { ItemIDs []uuid.UUID `json:"item_ids"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.store.UpdateItemOrder(c.Request.Context(), portfolioID, req.ItemIDs); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "items reordered"}) } // ============================================================================ // Merge Operations // ============================================================================ // MergePortfolios merges two portfolios // POST /sdk/v1/portfolios/merge func (h *PortfolioHandlers) MergePortfolios(c *gin.Context) { var req portfolio.MergeRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } source, err := h.store.GetPortfolio(c.Request.Context(), req.SourcePortfolioID) if err != nil || source == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "source portfolio not found"}) return } target, err := h.store.GetPortfolio(c.Request.Context(), req.TargetPortfolioID) if err != nil || target == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "target portfolio not found"}) return } if req.Strategy == "" { req.Strategy = portfolio.MergeStrategyUnion } userID := rbac.GetUserID(c) result, err := h.store.MergePortfolios(c.Request.Context(), &req, userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "message": "portfolios merged", "result": result, }) }