using Campaign_Tracker.Server.Seed.Models; namespace Campaign_Tracker.Server.Seed; public sealed class InMemorySeedDataStore : ISeedDataStore { private readonly object _sync = new(); private readonly SeedDataSnapshot _snapshot = new(); public Task UpsertSeedDataAsync(SeedDataSet seedData, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { UpsertMissing(_snapshot.ReferenceValues, seedData.ReferenceValues, Clone); UpsertMissing(_snapshot.RequiredFieldRules, seedData.RequiredFieldRules, Clone); UpsertMissing(_snapshot.EscalationRules, seedData.EscalationRules, Clone); } return Task.CompletedTask; } public Task> GetReferenceValuesAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { return Task.FromResult>( _snapshot.ReferenceValues.Select(Clone).ToArray()); } } public Task> GetRequiredFieldRulesAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { return Task.FromResult>( _snapshot.RequiredFieldRules.Select(Clone).ToArray()); } } public Task> GetEscalationRulesAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { return Task.FromResult>( _snapshot.EscalationRules.Select(Clone).ToArray()); } } public Task SaveReferenceValueAsync(ReferenceValue referenceValue, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { ReplaceBySeedKey(_snapshot.ReferenceValues, referenceValue, Clone); } return Task.CompletedTask; } public Task SaveRequiredFieldRuleAsync(RequiredFieldRule rule, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { ReplaceBySeedKey(_snapshot.RequiredFieldRules, rule, Clone); } return Task.CompletedTask; } public Task SaveEscalationRuleAsync(EscalationRule rule, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); lock (_sync) { ReplaceBySeedKey(_snapshot.EscalationRules, rule, Clone); } return Task.CompletedTask; } internal static void UpsertMissing( List target, IEnumerable defaults, Func clone) where T : class { foreach (var item in defaults) { var key = GetSeedKey(item); if (target.Any(existing => SameSeedKey(GetSeedKey(existing), key))) { continue; } target.Add(clone(item)); } } internal static void ReplaceBySeedKey( List target, T item, Func clone) where T : class { var key = GetSeedKey(item); var existingIndex = target.FindIndex(existing => SameSeedKey(GetSeedKey(existing), key)); if (existingIndex >= 0) { target[existingIndex] = clone(item); } else { target.Add(clone(item)); } } internal static ReferenceValue Clone(ReferenceValue value) => new() { Id = value.Id, SeedKey = value.SeedKey, Category = value.Category, Name = value.Name, Description = value.Description, Value = value.Value, Source = value.Source, IsActive = value.IsActive, CreatedAt = value.CreatedAt, UpdatedAt = value.UpdatedAt, }; internal static RequiredFieldRule Clone(RequiredFieldRule rule) => new() { Id = rule.Id, SeedKey = rule.SeedKey, Name = rule.Name, Description = rule.Description, EntityType = rule.EntityType, FieldPath = rule.FieldPath, ReadinessFeatureKey = rule.ReadinessFeatureKey, IsRequired = rule.IsRequired, Source = rule.Source, IsActive = rule.IsActive, CreatedAt = rule.CreatedAt, UpdatedAt = rule.UpdatedAt, }; internal static EscalationRule Clone(EscalationRule rule) => new() { Id = rule.Id, SeedKey = rule.SeedKey, Name = rule.Name, Description = rule.Description, Scenario = rule.Scenario, TriggerCondition = rule.TriggerCondition, Action = rule.Action, MilestoneBasis = rule.MilestoneBasis, AlertWindow = rule.AlertWindow, Priority = rule.Priority, Source = rule.Source, IsActive = rule.IsActive, CreatedAt = rule.CreatedAt, UpdatedAt = rule.UpdatedAt, }; private static string GetSeedKey(T item) where T : class => item switch { ReferenceValue value => value.SeedKey, RequiredFieldRule rule => rule.SeedKey, EscalationRule rule => rule.SeedKey, _ => throw new InvalidOperationException($"Unsupported seed item type {typeof(T).Name}."), }; private static bool SameSeedKey(string left, string right) => string.Equals(left, right, StringComparison.OrdinalIgnoreCase); internal sealed class SeedDataSnapshot { public List ReferenceValues { get; init; } = []; public List RequiredFieldRules { get; init; } = []; public List EscalationRules { get; init; } = []; } }