using FluentValidation; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SeaHaven.Services.Configuration; using SeaHaven.Services.Interfaces; using System.Reflection; namespace SeaHaven.Services.DependencyInjection { public static class ServicesModule { private static readonly List ExcludedTypes = new(); public static IServiceCollection AddBusinessServices(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration); services.Configure(configuration.GetSection(JwtOptions.SectionName)); services.Configure(configuration.GetSection(ApprovalsOptions.SectionName)); services.Configure(configuration.GetSection(VendorPortalOptions.SectionName)); services.Configure(configuration.GetSection(VendorDocumentsOptions.SectionName)); services.AddOptions() .Bind(configuration.GetSection(WorkOrderWebhookOptions.SectionName)) .Validate( o => o.MaxBodyBytes is > 0 and <= 1_048_576 && o.AllowedClockSkewSeconds is >= 0 and <= 3600 && o.SecretCacheSeconds is > 0 and <= 3600, "WorkOrderWebhook size, clock-skew, and cache settings are out of range.") .Validate( o => !o.Enabled || (o.KeySecrets.Count > 0 && o.KeySecrets.All(kvp => !string.IsNullOrWhiteSpace(kvp.Key) && kvp.Key.Length <= 128 && !string.IsNullOrWhiteSpace(kvp.Value))), "WorkOrderWebhook requires a non-empty key ID to secret ARN map when enabled.") .ValidateOnStart(); services.AddOptions() .Bind(configuration.GetSection(WorkOrderReconciliationOptions.SectionName)) .Validate( o => !o.Enabled || (o.BaseUrl == "https://procurement-api.seahaven.com" && o.Region == "us-east-1" && o.PageSize is >= 1 and <= 500 && o.MaxPages is >= 1 and <= 100_000 && o.MaxCursorLength is >= 1 and <= 16_384 && o.RequestTimeoutSeconds is >= 1 and <= 120 && o.MaxRetries is >= 0 and <= 8 && o.RetryBaseDelayMilliseconds is >= 0 and <= 10_000 && o.MaxResponseBytes is >= 1 and <= 16_777_216 && o.PollSeconds is >= 1 and <= 300 && o.ScheduleMinutes is >= 1 and <= 10_080 && o.LeaseSeconds is >= 30 and <= 3_600), "WorkOrderReconciliation configuration is invalid.") .ValidateOnStart(); var assembly = Assembly.GetExecutingAssembly(); var allClasses = assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType && !ExcludedTypes.Contains(t)) .ToList(); foreach (var implementationType in allClasses) { var defaultInterface = implementationType.GetInterfaces() .FirstOrDefault(i => i.Name == $"I{implementationType.Name}"); if (defaultInterface != null) { services.AddScoped(defaultInterface, implementationType); } } services.AddValidatorsFromAssembly(assembly); services.AddScoped( sp => (IWorkOrderReconciliationRunner)sp.GetRequiredService()); return services; } } }