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.EnsureSchema(_connection);
            return _connection;
        }
    }

    public void CloseConnection()
    {
        lock (_lock)
        {
            _connection?.Dispose();
            _connection = null;
        }
    }

    public void Dispose() => CloseConnection();
}
