using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using ScadaLink.Commons.Interfaces.Services; namespace ScadaLink.StoreAndForward; public static class ServiceCollectionExtensions { public static IServiceCollection AddStoreAndForward(this IServiceCollection services) { services.AddSingleton(sp => { var options = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); return new StoreAndForwardStorage( $"Data Source={options.SqliteDbPath}", logger); }); services.AddSingleton(sp => { var storage = sp.GetRequiredService(); var options = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); var replication = sp.GetRequiredService(); // Audit Log #23 (M3 Bundle F): Wire the cached-call lifecycle // observer + site identity through DI so the S&F retry loop emits // per-attempt + terminal telemetry under the same TrackedOperationId // the script-thread CachedSubmit row used. Both bindings are // optional — when null the legacy pre-M3 retry behaviour is // preserved exactly (tests, central nodes without sites, hosts // that haven't called AddAuditLog). // // Site identity is resolved through the optional // IStoreAndForwardSiteContext binding (registered by the Host) to // avoid a project-reference cycle with HealthMonitoring's // ISiteIdentityProvider — HealthMonitoring already references S&F. var cachedCallObserver = sp.GetService(); var siteContext = sp.GetService(); var siteId = siteContext?.SiteId ?? string.Empty; return new StoreAndForwardService( storage, options, logger, replication, cachedCallObserver, siteId); }); services.AddSingleton(sp => { var options = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); return new ReplicationService(options, logger); }); return services; } public static IServiceCollection AddStoreAndForwardActors(this IServiceCollection services) { // Akka actor registration handled by Host component during actor system startup return services; } }