package poller import ( "context" "log" "sync" "time" "supply-intelligence/internal/discovery" ) // DiscoveryRuntime runs periodic discovery scans for all registered platforms. type DiscoveryRuntime struct { scheduler *discovery.DiscoveryScheduler interval time.Duration cancel context.CancelFunc wg sync.WaitGroup } // NewDiscoveryRuntime creates a discovery runtime with the given scheduler and interval. func NewDiscoveryRuntime(scheduler *discovery.DiscoveryScheduler, interval time.Duration) *DiscoveryRuntime { return &DiscoveryRuntime{scheduler: scheduler, interval: interval} } // Start begins periodic discovery scanning. Does nothing if already started. func (r *DiscoveryRuntime) Start(parent context.Context) bool { if r == nil || r.scheduler == nil || r.cancel != nil { return false } ctx, cancel := context.WithCancel(parent) r.cancel = cancel r.wg.Add(1) go func() { defer r.wg.Done() // Run an immediate first scan r.runScan(context.Background()) ticker := time.NewTicker(r.interval) defer ticker.Stop() for { select { case <-ticker.C: r.runScan(context.Background()) case <-ctx.Done(): log.Println("[discovery-runtime] stopped") return } } }() log.Printf("[discovery-runtime] started with interval=%v", r.interval) return true } // Stop halts periodic scanning. func (r *DiscoveryRuntime) Stop() { if r == nil || r.cancel == nil { return } r.cancel() r.wg.Wait() } func (r *DiscoveryRuntime) runScan(ctx context.Context) { results, err := r.scheduler.ScanAllPlatforms(ctx) if err != nil { log.Printf("[discovery-runtime] scan error: %v", err) return } for _, res := range results { if len(res.Errors) > 0 { log.Printf("[discovery-runtime] platform=%s errors=%v", res.Platform, res.Errors) } else if res.NewModels > 0 { log.Printf("[discovery-runtime] platform=%s new_models=%d", res.Platform, res.NewModels) } } }