package handler import ( "bugulma/backend/internal/domain" "bugulma/backend/internal/service" "net/http" "strconv" "time" "github.com/gin-gonic/gin" ) // GraphTraversalHandler handles advanced graph traversal operations type GraphTraversalHandler struct { traversalService *service.GraphTraversalService } // NewGraphTraversalHandler creates a new graph traversal handler func NewGraphTraversalHandler(traversalService *service.GraphTraversalService) *GraphTraversalHandler { return &GraphTraversalHandler{ traversalService: traversalService, } } // FindResourceChains finds chains of resource flows from waste to reuse // @Summary Find resource chains // @Tags graph-traversal // @Produce json // @Param resourceType query string true "Resource type (e.g., biowaste, heat, water)" // @Param maxLength query int false "Maximum chain length" default(5) // @Param minValue query number false "Minimum economic value" default(1000) // @Success 200 {object} map[string]interface{} // @Router /api/graph/resource-chains [get] func (h *GraphTraversalHandler) FindResourceChains(c *gin.Context) { resourceTypeStr := c.Query("resourceType") if resourceTypeStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "resourceType parameter is required"}) return } resourceType := domain.ResourceType(resourceTypeStr) maxLengthStr := c.DefaultQuery("maxLength", "5") minValueStr := c.DefaultQuery("minValue", "1000") maxLength, err := strconv.Atoi(maxLengthStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxLength parameter"}) return } minValue, err := strconv.ParseFloat(minValueStr, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid minValue parameter"}) return } chains, err := h.traversalService.FindResourceChains(c.Request.Context(), resourceType, maxLength, minValue) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "resource_chains": chains, "total_chains": len(chains), "resource_type": resourceType, "criteria": gin.H{ "max_length": maxLength, "min_value": minValue, }, }) } // FindSymbiosisNetworks identifies interconnected industrial symbiosis networks // @Summary Find symbiosis networks // @Tags graph-traversal // @Produce json // @Param minOrganizations query int false "Minimum organizations per network" default(3) // @Param maxNetworkSize query int false "Maximum network size" default(20) // @Success 200 {object} map[string]interface{} // @Router /api/graph/symbiosis-networks [get] func (h *GraphTraversalHandler) FindSymbiosisNetworks(c *gin.Context) { minOrgsStr := c.DefaultQuery("minOrganizations", "3") maxSizeStr := c.DefaultQuery("maxNetworkSize", "20") minOrgs, err := strconv.Atoi(minOrgsStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid minOrganizations parameter"}) return } maxSize, err := strconv.Atoi(maxSizeStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxNetworkSize parameter"}) return } networks, err := h.traversalService.FindSymbiosisNetworks(c.Request.Context(), minOrgs, maxSize) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "symbiosis_networks": networks, "total_networks": len(networks), "criteria": gin.H{ "min_organizations": minOrgs, "max_network_size": maxSize, }, }) } // FindOptimalResourcePaths finds the most cost-effective paths for resource flows // @Summary Find optimal resource paths // @Tags graph-traversal // @Produce json // @Param sourceOrgId query string true "Source organization ID" // @Param targetOrgId query string true "Target organization ID" // @Param resourceType query string true "Resource type" // @Param maxHops query int false "Maximum hops in path" default(4) // @Success 200 {object} map[string]interface{} // @Router /api/graph/optimal-paths [get] func (h *GraphTraversalHandler) FindOptimalResourcePaths(c *gin.Context) { sourceOrgID := c.Query("sourceOrgId") targetOrgID := c.Query("targetOrgId") resourceTypeStr := c.Query("resourceType") maxHopsStr := c.DefaultQuery("maxHops", "4") if sourceOrgID == "" || targetOrgID == "" || resourceTypeStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "sourceOrgId, targetOrgId, and resourceType parameters are required"}) return } resourceType := domain.ResourceType(resourceTypeStr) maxHops, err := strconv.Atoi(maxHopsStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid maxHops parameter"}) return } paths, err := h.traversalService.FindOptimalResourcePaths(c.Request.Context(), sourceOrgID, targetOrgID, resourceType, maxHops) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "optimal_paths": paths, "total_paths": len(paths), "route": gin.H{ "source_org": sourceOrgID, "target_org": targetOrgID, "resource_type": resourceType, "max_hops": maxHops, }, }) } // AnalyzeNetworkCentrality calculates centrality measures for organizations // @Summary Analyze network centrality // @Tags graph-traversal // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/graph/centrality [get] func (h *GraphTraversalHandler) AnalyzeNetworkCentrality(c *gin.Context) { centrality, err := h.traversalService.AnalyzeNetworkCentrality(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Convert to response format var sortedOrgs []map[string]interface{} for orgID, metrics := range centrality { sortedOrgs = append(sortedOrgs, map[string]interface{}{ "org_id": orgID, "degree": metrics.Degree, "betweenness": metrics.Betweenness, "closeness": metrics.Closeness, "out_flows": metrics.OutFlows, "in_flows": metrics.InFlows, }) } // Sort by degree centrality (descending) for i := 0; i < len(sortedOrgs)-1; i++ { for j := i + 1; j < len(sortedOrgs); j++ { if sortedOrgs[i]["degree"].(float64) < sortedOrgs[j]["degree"].(float64) { sortedOrgs[i], sortedOrgs[j] = sortedOrgs[j], sortedOrgs[i] } } } c.JSON(http.StatusOK, gin.H{ "centrality_analysis": gin.H{ "organizations": sortedOrgs, "total_analyzed": len(sortedOrgs), "metrics": []string{"degree", "betweenness", "closeness"}, }, "interpretation": gin.H{ "degree_centrality": "Number of direct connections (local importance)", "betweenness_centrality": "Control over resource flows (network influence)", "closeness_centrality": "Average distance to other organizations (accessibility)", }, }) } // GetNetworkStatistics provides overall network statistics // @Summary Get network statistics // @Tags graph-traversal // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/graph/statistics [get] func (h *GraphTraversalHandler) GetNetworkStatistics(c *gin.Context) { stats, err := h.traversalService.GetNetworkStatistics(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "network_statistics": gin.H{ "total_organizations": stats.TotalOrganizations, "total_resource_flows": stats.TotalResourceFlows, "total_matches": stats.TotalMatches, "total_economic_value": stats.TotalEconomicValue, "avg_distance_km": stats.AverageDistance, "high_value_matches": stats.HighValueMatches, "geographic_span_km": stats.GeographicSpanKm, }, "generated_at": time.Now().Format(time.RFC3339), }) } // GetCircularEconomyAnalysis analyzes circular economy potential // @Summary Analyze circular economy potential // @Tags graph-traversal // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/graph/circular-economy [get] func (h *GraphTraversalHandler) GetCircularEconomyAnalysis(c *gin.Context) { cycles, err := h.traversalService.FindCircularEconomyCycles(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } totalCircularValue := 0.0 for _, cycle := range cycles { totalCircularValue += cycle.CycleValue } avgCycleValue := 0.0 if len(cycles) > 0 { avgCycleValue = totalCircularValue / float64(len(cycles)) } c.JSON(http.StatusOK, gin.H{ "circular_economy_analysis": gin.H{ "circular_cycles": cycles, "total_cycles": len(cycles), "total_circular_value": totalCircularValue, "avg_cycle_value": avgCycleValue, "circular_efficiency": totalCircularValue / 100000.0, // Simplified metric }, "analysis_timestamp": time.Now().Format(time.RFC3339), }) }