using Microsoft.Data.Sqlite; namespace Journal.Core.Services.Database; public sealed class DatabaseSessionService(IJournalDatabaseService database) : IDatabaseSessionService, IDisposable { private readonly IJournalDatabaseService _database = database; private readonly Lock _lock = new(); private string? _password; private SqliteConnection? _connection; public bool IsUnlocked { get { lock (_lock) { return _password is not null; } } } public void SetPassword(string password) { if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Password cannot be empty.", nameof(password)); lock (_lock) { if (_connection is not null && _password != password) { _connection.Dispose(); _connection = null; } _password = password; } } public bool TryGetSession(out string password) { lock (_lock) { if (string.IsNullOrWhiteSpace(_password)) { password = ""; return false; } password = _password; return true; } } public SqliteConnection GetConnection() { lock (_lock) { if (_password is null) throw new InvalidOperationException( "Database is locked. Authenticate first (e.g. vault.load_all or db.hydrate_workspace)."); if (_connection is not null) return _connection; _connection = _database.OpenEncryptedConnection(_password); _database.EnsureSchemaReady(_connection); return _connection; } } public void CloseConnection() { lock (_lock) { _connection?.Dispose(); _connection = null; } } public void Dispose() => CloseConnection(); }