using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace FoxBrowser.Core;
///
/// Snapshot of a single tab persisted to disk.
/// All properties are nullable so old session files remain forward-compatible.
///
public sealed class TabSnapshot
{
public string Address { get; init; } = "https://www.google.com";
public string Title { get; init; } = "Нова вкладка";
public bool IsAudioOnly { get; init; }
public bool IsDarkMode { get; init; }
public double DarkBrightness { get; init; } = 0.72;
public double DarkContrast { get; init; } = 1.08;
public double DarkSaturation { get; init; } = 0.88;
public double ScrollY { get; init; }
public double MediaPosition { get; init; }
public bool WasPlaying { get; init; }
}
public sealed class Session
{
public List Tabs { get; init; } = new();
public int ActiveTabIndex { get; init; }
}
///
/// Saves and loads the browser session (open tabs + per-tab state) to a JSON
/// file in the portable FoxBrowser folder next to the executable.
///
/// Crash-recovery strategy: the file is re-written after every successful
/// NavigationCompleted so a hard crash never loses more than one page load.
///
public static class SessionStore
{
private static readonly string SessionPath = Path.Combine(AppContext.BaseDirectory, "session.json");
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
};
public static async Task SaveAsync(IEnumerable tabs, int activeIndex)
{
try
{
var snapshots = tabs.Select(t => new TabSnapshot
{
Address = t.Address,
Title = t.Title,
IsAudioOnly = t.IsAudioOnly,
IsDarkMode = t.IsDarkMode,
DarkBrightness = t.DarkBrightness,
DarkContrast = t.DarkContrast,
DarkSaturation = t.DarkSaturation,
ScrollY = t.ScrollY,
MediaPosition = t.MediaPosition,
WasPlaying = t.WasPlaying,
}).ToList();
if (snapshots.Count == 0) return;
var session = new Session
{
Tabs = snapshots,
ActiveTabIndex = Math.Clamp(activeIndex, 0, snapshots.Count - 1),
};
Directory.CreateDirectory(Path.GetDirectoryName(SessionPath)!);
var json = JsonSerializer.Serialize(session, JsonOptions);
// Write to a temp file first, then replace atomically — avoids
// truncating the real file if the process is killed mid-write.
var tmp = SessionPath + ".tmp";
await File.WriteAllTextAsync(tmp, json);
File.Move(tmp, SessionPath, overwrite: true);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[SessionStore] Save failed: {ex.Message}");
}
}
public static async Task LoadAsync()
{
if (!File.Exists(SessionPath)) return null;
try
{
var json = await File.ReadAllTextAsync(SessionPath);
return JsonSerializer.Deserialize(json);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[SessionStore] Load failed: {ex.Message}");
return null;
}
}
}