package handlers import ( "net/http" "github.com/breakpilot/ai-compliance-sdk/internal/rbac" "github.com/breakpilot/ai-compliance-sdk/internal/roadmap" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // RoadmapHandlers handles roadmap-related HTTP requests type RoadmapHandlers struct { store *roadmap.Store parser *roadmap.Parser } // NewRoadmapHandlers creates new roadmap handlers func NewRoadmapHandlers(store *roadmap.Store) *RoadmapHandlers { return &RoadmapHandlers{ store: store, parser: roadmap.NewParser(), } } // ============================================================================ // Roadmap CRUD // ============================================================================ // CreateRoadmap creates a new roadmap // POST /sdk/v1/roadmaps func (h *RoadmapHandlers) CreateRoadmap(c *gin.Context) { var req roadmap.CreateRoadmapRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tenantID := rbac.GetTenantID(c) userID := rbac.GetUserID(c) r := &roadmap.Roadmap{ TenantID: tenantID, Title: req.Title, Description: req.Description, AssessmentID: req.AssessmentID, PortfolioID: req.PortfolioID, StartDate: req.StartDate, TargetDate: req.TargetDate, Status: "draft", CreatedBy: userID, } if err := h.store.CreateRoadmap(c.Request.Context(), r); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, roadmap.CreateRoadmapResponse{Roadmap: *r}) } // ListRoadmaps lists roadmaps for the tenant // GET /sdk/v1/roadmaps func (h *RoadmapHandlers) ListRoadmaps(c *gin.Context) { tenantID := rbac.GetTenantID(c) filters := &roadmap.RoadmapFilters{ Status: c.Query("status"), Limit: 50, } if assessmentID := c.Query("assessment_id"); assessmentID != "" { if id, err := uuid.Parse(assessmentID); err == nil { filters.AssessmentID = &id } } roadmaps, err := h.store.ListRoadmaps(c.Request.Context(), tenantID, filters) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "roadmaps": roadmaps, "total": len(roadmaps), }) } // GetRoadmap retrieves a roadmap by ID // GET /sdk/v1/roadmaps/:id func (h *RoadmapHandlers) GetRoadmap(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } r, err := h.store.GetRoadmap(c.Request.Context(), id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if r == nil { c.JSON(http.StatusNotFound, gin.H{"error": "roadmap not found"}) return } // Get items items, err := h.store.ListItems(c.Request.Context(), id, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Get stats stats, _ := h.store.GetRoadmapStats(c.Request.Context(), id) c.JSON(http.StatusOK, gin.H{ "roadmap": r, "items": items, "stats": stats, }) } // UpdateRoadmap updates a roadmap // PUT /sdk/v1/roadmaps/:id func (h *RoadmapHandlers) UpdateRoadmap(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } r, err := h.store.GetRoadmap(c.Request.Context(), id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if r == nil { c.JSON(http.StatusNotFound, gin.H{"error": "roadmap not found"}) return } var req roadmap.CreateRoadmapRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } r.Title = req.Title r.Description = req.Description r.AssessmentID = req.AssessmentID r.PortfolioID = req.PortfolioID r.StartDate = req.StartDate r.TargetDate = req.TargetDate if err := h.store.UpdateRoadmap(c.Request.Context(), r); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"roadmap": r}) } // DeleteRoadmap deletes a roadmap // DELETE /sdk/v1/roadmaps/:id func (h *RoadmapHandlers) DeleteRoadmap(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } if err := h.store.DeleteRoadmap(c.Request.Context(), id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "roadmap deleted"}) } // GetRoadmapStats returns statistics for a roadmap // GET /sdk/v1/roadmaps/:id/stats func (h *RoadmapHandlers) GetRoadmapStats(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } stats, err := h.store.GetRoadmapStats(c.Request.Context(), id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, stats) }