using System.Net.Http.Json; using System.Text.Json; using Microsoft.EntityFrameworkCore; using UtopiaCanteenSystem.Data; using UtopiaCanteenSystem.Models; using System.Net.Http; namespace UtopiaCanteenSystem.Services; /// /// Scheduled sync: fetches unsynced ScanRecords, POSTs them to the configured API, /// and deletes uploaded records from local SQLite on success. /// public class SyncService : ISyncService { private readonly IDbContextFactory _dbFactory; private readonly IConfigService _configService; private static readonly HttpClient HttpClient = new(); public SyncService(IDbContextFactory dbFactory, IConfigService configService) { _dbFactory = dbFactory; _configService = configService; } public async Task SyncNowAsync(CancellationToken cancellationToken = default) { var endpoint = _configService.GetSyncApiEndpoint(); if (string.IsNullOrWhiteSpace(endpoint)) return; List toSync; using (var db = _dbFactory.CreateDbContext()) { toSync = await db.ScanRecords .Where(r => !r.IsSynced) .OrderBy(r => r.ScanTime) .ToListAsync(cancellationToken) .ConfigureAwait(false); } if (toSync.Count == 0) return; var payload = toSync.Select(r => new { DeviceLocalRowId = r.Id, ScanTimeUtc = r.ScanTime, SiteId = r.SiteId ?? string.Empty, DeviceId = r.DeviceId ?? string.Empty, r.CardId }).ToList(); try { var response = await HttpClient .PostAsJsonAsync(endpoint, payload, cancellationToken: cancellationToken) .ConfigureAwait(false); if (!response.IsSuccessStatusCode) return; var ids = toSync.Select(r => r.Id).ToList(); using (var db = _dbFactory.CreateDbContext()) { var records = await db.ScanRecords .Where(r => ids.Contains(r.Id)) .ToListAsync(cancellationToken) .ConfigureAwait(false); // On successful upload, delete uploaded scan records from local SQLite. db.ScanRecords.RemoveRange(records); await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } } catch { // Leave records intact; will retry on next run } } }