using System.Windows; using System.Threading; using Microsoft.EntityFrameworkCore; using UtopiaCanteenSystem.Data; using UtopiaCanteenSystem.Services; using UtopiaCanteenSystem.ViewModels; namespace UtopiaCanteenSystem; /// /// Application entry point. Initializes database, builds service graph, starts hourly sync timer. /// public partial class App : Application { private System.Timers.Timer? _syncTimer; private int _isSyncRunning; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // Build services (simple composition; no DI container) var dbFactory = new DbContextFactory(); // Auto-create SQLite database on first run using (var db = dbFactory.CreateDbContext()) { db.EnsureDatabaseCreated(); } var configService = new ConfigService(); var rfidService = new RfidService(dbFactory, configService); var syncService = new SyncService(dbFactory, configService); var adminAuditService = new AdminAuditService(dbFactory); var session = new AppSession(); var authenticationUrl = "https://portal.utopiaindustries.pk/uind/rest/auth/user/"; var authService = new AuthService(authenticationUrl); // NavigationService: declare first so lambdas can capture it, then assign (avoids "used before declared") NavigationService navigationService = null!; navigationService = new NavigationService( session, () => new AdminLoginViewModel(authService, session, navigationService, configService, adminAuditService), () => new ScannerDashboardViewModel(rfidService, navigationService, session, configService), () => new MainDashboardViewModel(navigationService, rfidService, configService, session), () => new AdminSettingsAuthViewModel(authService, session, navigationService, configService), () => new SettingsViewModel(configService, navigationService, adminAuditService)); var mainViewModel = new MainViewModel(navigationService); var mainWindow = new MainWindow { DataContext = mainViewModel }; mainWindow.Show(); // Background sync: every 15 minutes, POST unsynced ScanRecords to API _syncTimer = new System.Timers.Timer(TimeSpan.FromMinutes(15).TotalMilliseconds) { AutoReset = true }; _syncTimer.Elapsed += async (_, _) => { // Prevent overlapping sync runs; if one is still running, skip this tick. if (Interlocked.Exchange(ref _isSyncRunning, 1) == 1) return; try { await syncService.SyncNowAsync().ConfigureAwait(false); } catch { // Ignore; will retry next tick } finally { Interlocked.Exchange(ref _isSyncRunning, 0); } }; _syncTimer.Start(); } protected override void OnExit(ExitEventArgs e) { _syncTimer?.Stop(); _syncTimer?.Dispose(); base.OnExit(e); } }