using Manager.DTOs; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.DTOs; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; namespace ManagerService.Services { public class AgendaSyncService { private readonly ILogger _logger; private readonly IServiceScopeFactory _scopeFactory; private readonly IHttpClientFactory _httpClientFactory; public AgendaSyncService(ILogger logger, IServiceScopeFactory scopeFactory, IHttpClientFactory httpClientFactory) { _logger = logger; _scopeFactory = scopeFactory; _httpClientFactory = httpClientFactory; } public async Task SyncAllAsync() { using var scope = _scopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var sections = db.Sections.OfType() .Where(sa => sa.IsOnlineAgenda && sa.AgendaResourceIds != null && sa.AgendaResourceIds.Count > 0) .Select(sa => sa.Id) .ToList(); foreach (var id in sections) { try { await SyncSectionAsync(id); } catch (Exception ex) { _logger.LogError(ex, "Error syncing agenda section {Id}", id); } } } public async Task SyncSectionAsync(string sectionAgendaId) { using var scope = _scopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var section = db.Sections.OfType() .Include(sa => sa.EventAgendas) .FirstOrDefault(sa => sa.Id == sectionAgendaId); if (section == null || !section.IsOnlineAgenda || section.AgendaResourceIds == null) return; var http = _httpClientFactory.CreateClient(); foreach (var resourceRef in section.AgendaResourceIds) { if (string.IsNullOrEmpty(resourceRef.value) || string.IsNullOrEmpty(resourceRef.language)) continue; var resource = db.Resources.FirstOrDefault(r => r.Id == resourceRef.value); if (resource == null || string.IsNullOrEmpty(resource.Url)) continue; List remoteEvents; try { var json = await http.GetStringAsync(resource.Url); remoteEvents = JsonConvert.DeserializeObject>(json, RemoteAgendaJsonSettings.Lenient) ?? new(); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to fetch agenda JSON for section {Id} language {Lang}", sectionAgendaId, resourceRef.language); continue; } foreach (var remote in remoteEvents) { var dateFrom = remote.GetDateFrom(); // Match on DateFrom (date part only), restricted to IsSynced events var existing = section.EventAgendas.FirstOrDefault(ea => ea.IsSynced && ea.DateFrom.HasValue && dateFrom.HasValue && ea.DateFrom.Value.Date == dateFrom.Value.Date); if (existing == null) { existing = new EventAgenda { Label = new List(), Description = new List(), SectionAgendaId = sectionAgendaId, IsSynced = true, }; section.EventAgendas.Add(existing); db.EventAgendas.Add(existing); } // Update / set translation for this language SetTranslation(existing.Label, resourceRef.language, remote.name); SetTranslation(existing.Description, resourceRef.language, remote.description); // Non-translated fields (last language wins, acceptable) existing.DateFrom = dateFrom; existing.DateTo = remote.GetDateTo(); existing.Phone = remote.phone; existing.Email = remote.email; existing.Website = remote.website; existing.IdVideoYoutube = remote.id_video_youtube; existing.IsSynced = true; } } db.SaveChanges(); _logger.LogInformation("Synced agenda section {Id}", sectionAgendaId); } private static void SetTranslation(List list, string language, string? value) { var existing = list.FirstOrDefault(t => t.language == language); if (existing != null) existing.value = value ?? ""; else list.Add(new TranslationDTO { language = language, value = value ?? "" }); } } }