package handlers import ( "encoding/json" "net/http" "github.com/breakpilot/ai-compliance-sdk/internal/ucca" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // RegistrationHandlers handles EU AI Database registration endpoints type RegistrationHandlers struct { store *ucca.RegistrationStore uccaStore *ucca.Store } // NewRegistrationHandlers creates new registration handlers func NewRegistrationHandlers(store *ucca.RegistrationStore, uccaStore *ucca.Store) *RegistrationHandlers { return &RegistrationHandlers{store: store, uccaStore: uccaStore} } // Create creates a new registration func (h *RegistrationHandlers) Create(c *gin.Context) { var reg ucca.AIRegistration if err := c.ShouldBindJSON(®); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()}) return } tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID")) if tenantID == uuid.Nil { c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"}) return } reg.TenantID = tenantID if reg.SystemName == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "system_name required"}) return } if err := h.store.Create(c.Request.Context(), ®); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create registration: " + err.Error()}) return } c.JSON(http.StatusCreated, reg) } // List lists all registrations for the tenant func (h *RegistrationHandlers) List(c *gin.Context) { tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID")) if tenantID == uuid.Nil { c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"}) return } registrations, err := h.store.List(c.Request.Context(), tenantID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list registrations: " + err.Error()}) return } if registrations == nil { registrations = []ucca.AIRegistration{} } c.JSON(http.StatusOK, gin.H{"registrations": registrations, "total": len(registrations)}) } // Get returns a single registration func (h *RegistrationHandlers) Get(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) return } reg, err := h.store.GetByID(c.Request.Context(), id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"}) return } c.JSON(http.StatusOK, reg) } // Update updates a registration func (h *RegistrationHandlers) Update(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) return } existing, err := h.store.GetByID(c.Request.Context(), id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"}) return } var updates ucca.AIRegistration if err := c.ShouldBindJSON(&updates); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()}) return } // Merge updates into existing updates.ID = existing.ID updates.TenantID = existing.TenantID updates.CreatedAt = existing.CreatedAt if err := h.store.Update(c.Request.Context(), &updates); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update: " + err.Error()}) return } c.JSON(http.StatusOK, updates) } // UpdateStatus changes the registration status func (h *RegistrationHandlers) UpdateStatus(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) return } var body struct { Status string `json:"status"` SubmittedBy string `json:"submitted_by"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) return } validStatuses := map[string]bool{ "draft": true, "ready": true, "submitted": true, "registered": true, "update_required": true, "withdrawn": true, } if !validStatuses[body.Status] { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid status. Valid: draft, ready, submitted, registered, update_required, withdrawn"}) return } if err := h.store.UpdateStatus(c.Request.Context(), id, body.Status, body.SubmittedBy); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update status: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"id": id, "status": body.Status}) } // Prefill creates a registration pre-filled from a UCCA assessment func (h *RegistrationHandlers) Prefill(c *gin.Context) { assessmentID, err := uuid.Parse(c.Param("assessment_id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"}) return } tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID")) if tenantID == uuid.Nil { c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"}) return } // Load UCCA assessment assessment, err := h.uccaStore.GetAssessment(c.Request.Context(), assessmentID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"}) return } // Extract intake data var intake ucca.UseCaseIntake if assessment.Intake != nil { json.Unmarshal(assessment.Intake, &intake) } // Pre-fill registration from assessment reg := ucca.AIRegistration{ TenantID: tenantID, SystemName: intake.Title, SystemDescription: intake.UseCaseText, IntendedPurpose: intake.UseCaseText, RiskClassification: string(assessment.RiskLevel), GPAIClassification: "none", RegistrationStatus: "draft", UCCAAssessmentID: &assessmentID, } // Map domain to readable text if intake.Domain != "" { reg.IntendedPurpose = string(intake.Domain) + ": " + intake.UseCaseText } c.JSON(http.StatusOK, reg) } // Export generates the EU AI Database submission JSON func (h *RegistrationHandlers) Export(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) return } reg, err := h.store.GetByID(c.Request.Context(), id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Registration not found"}) return } exportJSON := h.store.BuildExportJSON(reg) // Save export data to DB reg.ExportData = exportJSON h.store.Update(c.Request.Context(), reg) c.Header("Content-Type", "application/json") c.Header("Content-Disposition", "attachment; filename=eu_ai_registration_"+reg.SystemName+".json") c.Data(http.StatusOK, "application/json", exportJSON) }