using System.IO; using System.Text.Json; using UtopiaCanteenSystem.Data; using UtopiaCanteenSystem.Models; namespace UtopiaCanteenSystem.Services; /// /// Scanner frontend configuration: backend URL, device/site, admin UI. No HRMS/production credentials. /// public sealed class ClientConfigService : IConfigService { private readonly string _configPath; private ClientAppConfig _config; public ClientConfigService() { DatabasePath.UseClientStorage(); _configPath = DatabasePath.GetConfigPath(); _config = LoadConfig(); } public string GetSyncApiEndpoint() => string.Empty; public void SetSyncApiEndpoint(string endpoint) { } public bool GetSyncServiceEnabled() => false; public void SetSyncServiceEnabled(bool enabled) { } public bool GetScannerConnected() => _config.ScannerConnected; public void SetScannerConnected(bool connected) { _config.ScannerConnected = connected; SaveConfig(); } public int GetScanIntervalDays() => Math.Clamp(_config.ScanIntervalDays, 0, 365); public void SetScanIntervalDays(int value) { _config.ScanIntervalDays = Math.Clamp(value, 0, 365); SaveConfig(); } public int GetScanIntervalHours() => Math.Clamp(_config.ScanIntervalHours, 0, 23); public void SetScanIntervalHours(int value) { _config.ScanIntervalHours = Math.Clamp(value, 0, 23); SaveConfig(); } public int GetScanIntervalMinutes() => Math.Clamp(_config.ScanIntervalMinutes, 0, 59); public void SetScanIntervalMinutes(int value) { _config.ScanIntervalMinutes = Math.Clamp(value, 0, 59); SaveConfig(); } public int GetScanIntervalSeconds() => Math.Clamp(_config.ScanIntervalSeconds, 0, 59); public void SetScanIntervalSeconds(int value) { _config.ScanIntervalSeconds = Math.Clamp(value, 0, 59); SaveConfig(); } public TimeSpan GetScanInterval() { var ts = TimeSpan.FromDays(GetScanIntervalDays()) + TimeSpan.FromHours(GetScanIntervalHours()) + TimeSpan.FromMinutes(GetScanIntervalMinutes()) + TimeSpan.FromSeconds(GetScanIntervalSeconds()); return ts > TimeSpan.Zero ? ts : TimeSpan.FromMinutes(1); } public string GetAdminCardId() => _config.AdminCardId ?? "ADMIN"; public void SetAdminCardId(string cardId) { _config.AdminCardId = cardId ?? string.Empty; SaveConfig(); } public string GetSiteId() => _config.SiteId ?? string.Empty; public void SetSiteId(string siteId) { _config.SiteId = siteId ?? string.Empty; SaveConfig(); } public void ApplyLocationSiteIdFromAuth(string? locationSiteId) { if (string.IsNullOrWhiteSpace(locationSiteId)) return; var digits = new string(locationSiteId.Where(char.IsDigit).ToArray()); if (string.IsNullOrEmpty(digits)) return; _config.SiteId = $"SITE : {digits}"; SaveConfig(); } public string GetDeviceId() { if (!string.IsNullOrWhiteSpace(_config.DeviceId)) return _config.DeviceId.Trim(); _config.DeviceId = ResolveMachineDeviceId(); SaveConfig(); return _config.DeviceId; } public void SetDeviceId(string deviceId) { _config.DeviceId = string.IsNullOrWhiteSpace(deviceId) ? ResolveMachineDeviceId() : deviceId.Trim(); SaveConfig(); } public bool GetRememberAdminCredentials() => _config.RememberAdminCredentials; public void SetRememberAdminCredentials(bool remember) { _config.RememberAdminCredentials = remember; SaveConfig(); } public string GetSavedAdminUsername() => _config.SavedAdminUsername ?? string.Empty; public void SetSavedAdminUsername(string username) { _config.SavedAdminUsername = username ?? string.Empty; SaveConfig(); } public string GetSavedAdminPassword() => DecryptPassword(_config.SavedAdminPasswordProtected ?? string.Empty); public void SetSavedAdminPassword(string password) { _config.SavedAdminPasswordProtected = EncryptPassword(password ?? string.Empty); SaveConfig(); } public string GetMySqlConnectionString() => string.Empty; public void SetMySqlConnectionString(string connectionString) { } public string GetHrmsLookupConnectionString() => string.Empty; public void SetHrmsLookupConnectionString(string connectionString) { } public DateTime? GetLastEmployeeRfidCacheSyncUtc() => null; public void SetLastEmployeeRfidCacheSyncUtc(DateTime utc) { } public DateTime? GetLastMealMenuCacheSyncUtc() => null; public void SetLastMealMenuCacheSyncUtc(DateTime utc) { } public AppMode GetAppMode() => AppMode.Client; public void SetAppMode(AppMode mode) { } public string GetCentralServerBaseUrl() => GetBackendBaseUrl(); public void SetCentralServerBaseUrl(string url) => SetBackendBaseUrl(url); public string GetBackendBaseUrl() => (_config.BackendBaseUrl ?? string.Empty).TrimEnd('/'); public void SetBackendBaseUrl(string url) { _config.BackendBaseUrl = (url ?? string.Empty).Trim().TrimEnd('/'); SaveConfig(); } public string GetLocalServerListenUrls() => string.Empty; public void SetLocalServerListenUrls(string urls) { } private ClientAppConfig LoadConfig() { try { if (!File.Exists(_configPath)) return new ClientAppConfig { BackendBaseUrl = "http://localhost:5000" }; var json = File.ReadAllText(_configPath); return JsonSerializer.Deserialize(json) ?? new ClientAppConfig(); } catch { return new ClientAppConfig(); } } private void SaveConfig() { try { var json = JsonSerializer.Serialize(_config, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_configPath, json); } catch (Exception ex) { Logger.Log(ex, "ClientConfigService.SaveConfig"); } } private static string ResolveMachineDeviceId() { var machineName = Environment.MachineName; return string.IsNullOrWhiteSpace(machineName) ? Guid.NewGuid().ToString("N") : machineName.Trim(); } private static string EncryptPassword(string plain) { if (string.IsNullOrEmpty(plain)) return string.Empty; try { var bytes = System.Text.Encoding.UTF8.GetBytes(plain); var protectedBytes = System.Security.Cryptography.ProtectedData.Protect(bytes, null, System.Security.Cryptography.DataProtectionScope.CurrentUser); return Convert.ToBase64String(protectedBytes); } catch { return string.Empty; } } private static string DecryptPassword(string protectedBase64) { if (string.IsNullOrEmpty(protectedBase64)) return string.Empty; try { var protectedBytes = Convert.FromBase64String(protectedBase64); var bytes = System.Security.Cryptography.ProtectedData.Unprotect(protectedBytes, null, System.Security.Cryptography.DataProtectionScope.CurrentUser); return System.Text.Encoding.UTF8.GetString(bytes); } catch { return string.Empty; } } private sealed class ClientAppConfig { public string? BackendBaseUrl { get; set; } public bool ScannerConnected { get; set; } public int ScanIntervalDays { get; set; } public int ScanIntervalHours { get; set; } public int ScanIntervalMinutes { get; set; } public int ScanIntervalSeconds { get; set; } = 5; public string? AdminCardId { get; set; } public string? SiteId { get; set; } public string? DeviceId { get; set; } public bool RememberAdminCredentials { get; set; } public string? SavedAdminUsername { get; set; } public string? SavedAdminPasswordProtected { get; set; } } }