package handlers import ( "net/http" "github.com/breakpilot/ai-compliance-sdk/internal/iace" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // DeltaAnalysisRequest is the request body for POST /projects/:id/delta-analysis. type DeltaAnalysisRequest struct { // Current state — if empty, loaded from project's existing components/energy CurrentComponents []string `json:"current_components,omitempty"` CurrentEnergy []string `json:"current_energy,omitempty"` CurrentStates []string `json:"current_states,omitempty"` CurrentRoles []string `json:"current_roles,omitempty"` // Proposed additions AddComponents []string `json:"add_components,omitempty"` AddEnergy []string `json:"add_energy,omitempty"` AddStates []string `json:"add_states,omitempty"` AddRoles []string `json:"add_roles,omitempty"` // Proposed removals RemoveComponents []string `json:"remove_components,omitempty"` RemoveEnergy []string `json:"remove_energy,omitempty"` RemoveStates []string `json:"remove_states,omitempty"` RemoveRoles []string `json:"remove_roles,omitempty"` } // DeltaAnalysis handles POST /projects/:id/delta-analysis. // It computes the impact of a proposed change on the hazard/measure landscape. func (h *IACEHandler) DeltaAnalysis(c *gin.Context) { projectID, err := uuid.Parse(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"}) return } var req DeltaAnalysisRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Build current MatchInput — from request or from project's existing data currentComps := req.CurrentComponents currentEnergy := req.CurrentEnergy currentStates := req.CurrentStates currentRoles := req.CurrentRoles _ = projectID // Used for future: auto-load current state from project DB currentInput := iace.MatchInput{ ComponentLibraryIDs: currentComps, EnergySourceIDs: currentEnergy, OperationalStates: currentStates, HumanRoles: currentRoles, } // Build proposed MatchInput = current + additions - removals proposedComps := applyDelta(currentComps, req.AddComponents, req.RemoveComponents) proposedEnergy := applyDelta(currentEnergy, req.AddEnergy, req.RemoveEnergy) proposedStates := applyDelta(currentStates, req.AddStates, req.RemoveStates) proposedRoles := applyDelta(currentRoles, req.AddRoles, req.RemoveRoles) proposedInput := iace.MatchInput{ ComponentLibraryIDs: proposedComps, EnergySourceIDs: proposedEnergy, OperationalStates: proposedStates, HumanRoles: proposedRoles, } // Run delta analysis engine := iace.NewPatternEngine() result := engine.DeltaMatch(currentInput, proposedInput) c.JSON(http.StatusOK, result) } // applyDelta computes: current + additions - removals func applyDelta(current, add, remove []string) []string { removeSet := make(map[string]bool) for _, r := range remove { removeSet[r] = true } result := make([]string, 0, len(current)+len(add)) seen := make(map[string]bool) for _, s := range current { if !removeSet[s] && !seen[s] { result = append(result, s) seen[s] = true } } for _, s := range add { if !seen[s] { result = append(result, s) seen[s] = true } } return result }