using Microsoft.Data.Sqlite;

namespace ZB.MOM.WW.MxGateway.Server.Security.Authentication;

/// <summary>SQLite-based store for API key records.</summary>
public sealed class SqliteApiKeyStore(AuthSqliteConnectionFactory connectionFactory) : IApiKeyStore
{
    /// <inheritdoc />
    public Task<ApiKeyRecord?> FindByKeyIdAsync(string keyId, CancellationToken cancellationToken)
    {
        return FindByKeyIdAsync(keyId, requireActive: false, cancellationToken);
    }

    /// <inheritdoc />
    public Task<ApiKeyRecord?> FindActiveByKeyIdAsync(string keyId, CancellationToken cancellationToken)
    {
        return FindByKeyIdAsync(keyId, requireActive: true, cancellationToken);
    }

    /// <inheritdoc />
    public async Task MarkKeyUsedAsync(string keyId, DateTimeOffset usedUtc, CancellationToken cancellationToken)
    {
        await using SqliteConnection connection = await connectionFactory.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);

        await using SqliteCommand command = connection.CreateCommand();
        command.CommandText = """
            UPDATE api_keys
            SET last_used_utc = $last_used_utc
            WHERE key_id = $key_id AND revoked_utc IS NULL;
            """;
        command.Parameters.AddWithValue("$key_id", keyId);
        command.Parameters.AddWithValue("$last_used_utc", usedUtc.ToString("O"));

        await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
    }

    private async Task<ApiKeyRecord?> FindByKeyIdAsync(
        string keyId,
        bool requireActive,
        CancellationToken cancellationToken)
    {
        await using SqliteConnection connection = await connectionFactory.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);

        await using SqliteCommand command = connection.CreateCommand();
        command.CommandText = requireActive
            ? """
              SELECT key_id, key_prefix, secret_hash, display_name, scopes, constraints, created_utc, last_used_utc, revoked_utc
              FROM api_keys
              WHERE key_id = $key_id AND revoked_utc IS NULL;
              """
            : """
              SELECT key_id, key_prefix, secret_hash, display_name, scopes, constraints, created_utc, last_used_utc, revoked_utc
              FROM api_keys
              WHERE key_id = $key_id;
              """;
        command.Parameters.AddWithValue("$key_id", keyId);

        await using SqliteDataReader reader = await command.ExecuteReaderAsync(cancellationToken)
            .ConfigureAwait(false);

        if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
        {
            return null;
        }

        return ApiKeyRecordReader.Read(reader);
    }
}
