using System.Text.Json; using LyricFlow.Core.Dtos; using LyricFlow.Core.Serialization; namespace LyricFlow.Core.Services; public class SessionStoreService { public const string GlobalWorkspaceKey = "__global__"; private readonly string _storagePath; // MARK: - Lifecycle #region Lifecycle public SessionStoreService(string? storagePath = null) { _storagePath = storagePath ?? Path.Combine(AppPaths.AppDataDirectory(), "session_snapshots.json"); var directory = Path.GetDirectoryName(_storagePath); if (!string.IsNullOrWhiteSpace(directory)) { Directory.CreateDirectory(directory); } } #endregion // MARK: - Public Operations #region Public Operations public List Load(string? workspaceRoot) { var payload = ReadPayload(); return payload.Workspaces.TryGetValue(WorkspaceKey(workspaceRoot), out var snapshots) ? snapshots : []; } public void Save(string? workspaceRoot, List snapshots) { var payload = ReadPayload(); var key = WorkspaceKey(workspaceRoot); if (snapshots.Count == 0) { payload.Workspaces.Remove(key); } else { payload.Workspaces[key] = snapshots; } WritePayload(payload); } public void Clear(string? workspaceRoot = null) { if (workspaceRoot is null) { if (File.Exists(_storagePath)) { File.Delete(_storagePath); } return; } var payload = ReadPayload(); payload.Workspaces.Remove(WorkspaceKey(workspaceRoot)); if (payload.Workspaces.Count == 0) { if (File.Exists(_storagePath)) { File.Delete(_storagePath); } return; } WritePayload(payload); } #endregion // MARK: - Payload Persistence #region Payload Persistence private SessionPayload ReadPayload() { if (!File.Exists(_storagePath)) { return new SessionPayload(); } try { var payload = JsonSerializer.Deserialize(File.ReadAllText(_storagePath), LyricFlowCoreJsonContext.Default.SessionPayload); return payload ?? new SessionPayload(); } catch (IOException) { return new SessionPayload(); } catch (JsonException) { return new SessionPayload(); } } private void WritePayload(SessionPayload payload) { payload.Version = 1; File.WriteAllText(_storagePath, JsonSerializer.Serialize(payload, LyricFlowCoreJsonContext.Default.SessionPayload)); } #endregion // MARK: - Workspace Keys #region Workspace Keys private static string WorkspaceKey(string? workspaceRoot) { if (string.IsNullOrWhiteSpace(workspaceRoot)) { return GlobalWorkspaceKey; } var fullPath = Path.GetFullPath(workspaceRoot); return OperatingSystem.IsWindows() ? fullPath.ToLowerInvariant() : fullPath; } #endregion // MARK: - Payload Model #region Payload Model #endregion } internal sealed class SessionPayload { public int Version { get; set; } = 1; public Dictionary> Workspaces { get; set; } = new(StringComparer.OrdinalIgnoreCase); }