diff --git a/apps/carrier-connector/internal/integration/selection_integration.go b/apps/carrier-connector/internal/integration/selection_integration.go new file mode 100644 index 0000000..c7e9ad3 --- /dev/null +++ b/apps/carrier-connector/internal/integration/selection_integration.go @@ -0,0 +1,327 @@ +package integration + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/config" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/handlers" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/service" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/smdp" +) + +// SelectionIntegration wires together the carrier selection components +type SelectionIntegration struct { + manager *smdp.SMDPManager + selectionService *service.SelectionService + selectionHandler *handlers.SelectionHandler + smdpService *service.SMDPService + server *http.Server +} + +// NewSelectionIntegration creates a new selection integration +func NewSelectionIntegration(repo *repository.PostgresProfileStore) *SelectionIntegration { + // Create SMDP manager with default configuration + config := &smdp.ManagerConfig{ + HealthCheckInterval: 30 * time.Second, + EnableFailover: true, + EnableLoadBalancing: true, + MaxRetries: 3, + RetryDelay: 1 * time.Second, + } + + manager := smdp.NewSMDPManager(repo, config) + + // Create selection service + selectionService := service.NewSelectionService(manager) + + // Create SMDP service + smdpService := service.NewSMDPService(repo) + + return &SelectionIntegration{ + manager: manager, + selectionService: selectionService, + selectionHandler: selectionService.GetHandler(), + smdpService: smdpService, + } +} + +// SetupCarriers configures default carriers for demonstration +func (si *SelectionIntegration) SetupCarriers() error { + // Configure sample carriers with different characteristics + carriers := []*smdp.Carrier{ + { + ID: "att-us", + Name: "AT&T US", + MCC: "310", + MNC: "410", + CountryCode: "US", + IsActive: true, + Priority: 90, + ES2Config: &config.ES2Config{ + BaseURL: "https://es2plus.att.com", + APIKey: "demo-key-att", + InsecureSkipVerify: false, + FunctionalityRequesterID: "telecom-platform", + }, + Capabilities: &smdp.CarrierCapabilities{ + SupportedProfileTypes: []string{"operational", "testing"}, + Features: []string{"bulk_download", "remote_provisioning"}, + MaxConcurrentRequests: 100, + }, + Metrics: &smdp.CarrierMetrics{ + TotalRequests: 1000, + SuccessfulRequests: 980, + FailedRequests: 20, + AverageResponseTime: 150 * time.Millisecond, + RequestRate: 10.5, + }, + }, + { + ID: "verizon-us", + Name: "Verizon US", + MCC: "311", + MNC: "480", + CountryCode: "US", + IsActive: true, + Priority: 85, + ES2Config: &config.ES2Config{ + BaseURL: "https://es2plus.verizon.com", + APIKey: "demo-key-verizon", + InsecureSkipVerify: false, + FunctionalityRequesterID: "telecom-platform", + }, + Capabilities: &smdp.CarrierCapabilities{ + SupportedProfileTypes: []string{"operational", "testing"}, + Features: []string{"bulk_download"}, + MaxConcurrentRequests: 80, + }, + Metrics: &smdp.CarrierMetrics{ + TotalRequests: 800, + SuccessfulRequests: 790, + FailedRequests: 10, + AverageResponseTime: 120 * time.Millisecond, + RequestRate: 8.2, + }, + }, + { + ID: "tmobile-de", + Name: "T-Mobile Germany", + MCC: "262", + MNC: "01", + CountryCode: "DE", + IsActive: true, + Priority: 75, + ES2Config: &config.ES2Config{ + BaseURL: "https://es2plus.t-mobile.de", + APIKey: "demo-key-tmobile", + InsecureSkipVerify: false, + FunctionalityRequesterID: "telecom-platform", + }, + Capabilities: &smdp.CarrierCapabilities{ + SupportedProfileTypes: []string{"operational"}, + Features: []string{"remote_provisioning"}, + MaxConcurrentRequests: 60, + }, + Metrics: &smdp.CarrierMetrics{ + TotalRequests: 600, + SuccessfulRequests: 570, + FailedRequests: 30, + AverageResponseTime: 200 * time.Millisecond, + RequestRate: 6.8, + }, + }, + { + ID: "orange-fr", + Name: "Orange France", + MCC: "208", + MNC: "01", + CountryCode: "FR", + IsActive: true, + Priority: 70, + ES2Config: &config.ES2Config{ + BaseURL: "https://es2plus.orange.fr", + APIKey: "demo-key-orange", + InsecureSkipVerify: false, + FunctionalityRequesterID: "telecom-platform", + }, + Capabilities: &smdp.CarrierCapabilities{ + SupportedProfileTypes: []string{"operational", "testing"}, + Features: []string{}, + MaxConcurrentRequests: 50, + }, + Metrics: &smdp.CarrierMetrics{ + TotalRequests: 400, + SuccessfulRequests: 380, + FailedRequests: 20, + AverageResponseTime: 180 * time.Millisecond, + RequestRate: 4.5, + }, + }, + } + + // Add carriers to the manager + for _, carrier := range carriers { + if err := si.manager.AddCarrier(carrier); err != nil { + return err + } + } + + log.Printf("Added %d carriers to the selection manager", len(carriers)) + return nil +} + +// StartServer starts the HTTP server with all endpoints +func (si *SelectionIntegration) StartServer(port string) error { + // Create HTTP multiplexer + mux := http.NewServeMux() + + // Register selection routes + si.selectionHandler.RegisterRoutes(mux) + + // Add health check endpoint + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"status": "healthy", "service": "selection-integration"}`)) + }) + + // Create and configure server + si.server = &http.Server{ + Addr: ":" + port, + Handler: mux, + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, + } + + log.Printf("Starting selection integration server on port %s", port) + return si.server.ListenAndServe() +} + +// StartHealthChecking starts the background health checking process +func (si *SelectionIntegration) StartHealthChecking(ctx context.Context) { + si.manager.StartHealthChecking(ctx) +} + +// GetManager returns the SMDP manager for testing +func (si *SelectionIntegration) GetManager() *smdp.SMDPManager { + return si.manager +} + +// GetSelectionService returns the selection service for testing +func (si *SelectionIntegration) GetSelectionService() *service.SelectionService { + return si.selectionService +} + +// Shutdown gracefully shuts down the server +func (si *SelectionIntegration) Shutdown(ctx context.Context) error { + if si.server != nil { + return si.server.Shutdown(ctx) + } + return nil +} + +// RunDemo runs a demonstration of the carrier selection capabilities +func (si *SelectionIntegration) RunDemo(ctx context.Context) error { + log.Println("Starting carrier selection demonstration...") + + // Setup carriers + if err := si.SetupCarriers(); err != nil { + return err + } + + // Start health checking + si.StartHealthChecking(ctx) + + // Wait a moment for health checks to initialize + time.Sleep(2 * time.Second) + + // Demonstrate intelligent carrier selection + log.Println("Demonstrating intelligent carrier selection...") + + // Test different selection scenarios + scenarios := []struct { + name string + criteria *smdp.SelectionCriteria + }{ + { + name: "High Priority US Request", + criteria: &smdp.SelectionCriteria{ + Region: "US", + ProfileType: "operational", + Urgency: "high", + CostSensitivity: 0.2, + PerformanceWeight: 0.6, + ReliabilityWeight: 0.6, + }, + }, + { + name: "Cost-Optimized European Request", + criteria: &smdp.SelectionCriteria{ + Region: "DE", + ProfileType: "operational", + Urgency: "low", + CostSensitivity: 0.8, + PerformanceWeight: 0.2, + ReliabilityWeight: 0.3, + }, + }, + { + name: "Balanced Global Request", + criteria: &smdp.SelectionCriteria{ + Region: "", + ProfileType: "operational", + Urgency: "medium", + CostSensitivity: 0.5, + PerformanceWeight: 0.4, + ReliabilityWeight: 0.4, + }, + }, + } + + for _, scenario := range scenarios { + log.Printf("Testing scenario: %s", scenario.name) + + score, err := si.manager.SelectOptimalCarrier(ctx, scenario.criteria) + if err != nil { + log.Printf("Error in scenario %s: %v", scenario.name, err) + continue + } + + log.Printf("Selected carrier: %s (%s)", score.Carrier.Name, score.CarrierID) + log.Printf("Total score: %.2f", score.TotalScore) + log.Printf("Performance: %.2f, Reliability: %.2f, Cost: %.2f, Region: %.2f, Capability: %.2f", + score.PerformanceScore, score.ReliabilityScore, score.CostScore, score.RegionScore, score.CapabilityScore) + log.Printf("Reason: %s", score.Reason) + log.Println("---") + } + + // Demonstrate analytics + log.Println("Generating carrier analytics...") + analytics, err := si.selectionService.GetCarrierAnalytics(ctx) + if err != nil { + log.Printf("Error generating analytics: %v", err) + } else { + log.Printf("Analytics generated for %d carriers", analytics.TotalCarriers) + log.Printf("Overall health: %.1f%%", analytics.Summary.OverallHealth) + log.Printf("Overall success rate: %.1f%%", analytics.Summary.OverallSuccessRate) + } + + // Demonstrate optimization recommendations + log.Println("Generating optimization recommendations...") + optimization, err := si.selectionService.OptimizeCarrierSelection(ctx) + if err != nil { + log.Printf("Error generating optimization: %v", err) + } else { + log.Printf("System health: %.1f%%", optimization.OverallHealth) + log.Printf("Recommendations: %d", len(optimization.Recommendations)) + log.Printf("Priority actions: %d", len(optimization.PriorityActions)) + } + + log.Println("Carrier selection demonstration completed successfully!") + return nil +}