using System; using System.Collections.Generic; using System.Threading.Tasks; using FinlyticSentiment.Database; using FinlyticSentiment.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace FinlyticSentiment.Services; /// /// Interface for reading and persisting FinlyticSentiment runtime configuration settings in PostgreSQL. /// public interface ISettingsDbService { /// /// Retrieves current sentiment settings from PostgreSQL database, seeding defaults if empty. /// Task GetSettingsAsync(); /// /// Persists updated settings entity to PostgreSQL. /// Task SaveSettingsAsync(SentimentSettingsEntity settings); /// /// Updates settings from a key-value dictionary received via Admin Panel MQTT events. /// Task UpdateSettingsFromDictionaryAsync(Dictionary dictionary); } /// /// EF Core PostgreSQL implementation of . /// public class SettingsDbService : ISettingsDbService { private readonly SentimentDbContext _context; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// public SettingsDbService(SentimentDbContext context, ILogger logger) { _context = context; _logger = logger; } /// /// Retrieves current sentiment settings from PostgreSQL database. /// public async Task GetSettingsAsync() { var settings = await _context.Settings.AsNoTracking().FirstOrDefaultAsync(); if (settings == null) { settings = new SentimentSettingsEntity { Id = Guid.NewGuid() }; _context.Settings.Add(settings); await _context.SaveChangesAsync(); _context.ChangeTracker.Clear(); } return settings; } /// /// Persists updated settings entity to PostgreSQL. /// public async Task SaveSettingsAsync(SentimentSettingsEntity settings) { var existing = await _context.Settings.FirstOrDefaultAsync(); if (existing == null) { if (settings.Id == Guid.Empty) settings.Id = Guid.NewGuid(); _context.Settings.Add(settings); } else { existing.MinConfidenceScore = settings.MinConfidenceScore; existing.MaxBatchSize = settings.MaxBatchSize; existing.SweepIntervalMinutes = settings.SweepIntervalMinutes; existing.GermanWebhookUrl = settings.GermanWebhookUrl; existing.EnglishWebhookUrl = settings.EnglishWebhookUrl; existing.UpdatedAt = settings.UpdatedAt; _context.Settings.Update(existing); } await _context.SaveChangesAsync(); return settings; } /// /// Updates settings from a key-value dictionary. /// public async Task UpdateSettingsFromDictionaryAsync(Dictionary dictionary) { var settings = await GetSettingsAsync(); foreach (var (key, value) in dictionary) { if (string.Equals(key, "MinConfidenceScore", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out var mcs)) settings.MinConfidenceScore = mcs; else if (string.Equals(key, "MaxBatchSize", StringComparison.OrdinalIgnoreCase) && int.TryParse(value, out var mbs)) settings.MaxBatchSize = mbs; else if (string.Equals(key, "SweepIntervalMinutes", StringComparison.OrdinalIgnoreCase) && int.TryParse(value, out var sim)) settings.SweepIntervalMinutes = sim; else if (string.Equals(key, "GermanWebhookUrl", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(value)) settings.GermanWebhookUrl = value.Trim(); else if (string.Equals(key, "EnglishWebhookUrl", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(value)) settings.EnglishWebhookUrl = value.Trim(); } settings.UpdatedAt = DateTime.UtcNow; await SaveSettingsAsync(settings); _logger.LogInformation("[{Channel}] Successfully updated {Count} sentiment settings in PostgreSQL database.", "SentimentChannel", dictionary.Count); } }