using System.Text.Json; namespace ChangeT50.Agent; public sealed class LocalJournal { private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web) { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }; private readonly string _path; private readonly object _sync = new(); public LocalJournal(string path) { _path = Path.GetFullPath(path); } public JournalEntry? Get(string clientOrderNo) { lock (_sync) { return ReadAll().GetValueOrDefault(clientOrderNo); } } public void Save(JournalEntry entry) { ArgumentException.ThrowIfNullOrWhiteSpace(entry.ClientOrderNo); lock (_sync) { var entries = ReadAll(); entry.UpdatedAt = DateTimeOffset.Now; entries[entry.ClientOrderNo] = entry; WriteAll(entries); } } public int PendingUploadCount() { lock (_sync) { return ReadAll().Values.Count(entry => entry.State is "card_debited" or "upload_retrying"); } } private Dictionary ReadAll() { if (!File.Exists(_path)) { return new Dictionary(StringComparer.Ordinal); } var json = File.ReadAllText(_path); return JsonSerializer.Deserialize>(json, JsonOptions) ?? new Dictionary(StringComparer.Ordinal); } private void WriteAll(Dictionary entries) { var directory = Path.GetDirectoryName(_path); if (!string.IsNullOrEmpty(directory)) { Directory.CreateDirectory(directory); } var temporaryPath = _path + ".tmp"; File.WriteAllText(temporaryPath, JsonSerializer.Serialize(entries, JsonOptions)); File.Move(temporaryPath, _path, true); } }