using Microsoft.Data.SqlClient;

namespace ZB.MOM.WW.Secrets.Replicator.SqlServer;

/// <summary>
/// Opens connections to the shared SQL-Server secret database and carries the settings every
/// command needs (schema name, command timeout).
/// </summary>
/// <param name="options">The validated SQL-Server store options.</param>
public sealed class SecretsSqlServerConnectionFactory(SqlServerSecretsOptions options)
{
    private readonly SqlServerSecretsOptions _options =
        options ?? throw new ArgumentNullException(nameof(options));

    /// <summary>The bracket-quoted, allow-list-validated schema prefix for object names.</summary>
    public string SchemaName => _options.SchemaName;

    /// <summary>Command timeout, in whole seconds, for every command issued against this store.</summary>
    public int CommandTimeoutSeconds => Math.Max(1, (int)_options.CommandTimeout.TotalSeconds);

    /// <summary>Opens a connection to the shared secret database.</summary>
    /// <param name="cancellationToken">A token to cancel the connect.</param>
    /// <returns>An opened connection. The caller owns disposal.</returns>
    public async Task<SqlConnection> OpenConnectionAsync(CancellationToken cancellationToken)
    {
        var connection = new SqlConnection(_options.ConnectionString);
        try
        {
            await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
            return connection;
        }
        catch
        {
            await connection.DisposeAsync().ConfigureAwait(false);
            throw;
        }
    }

    /// <summary>
    /// Creates a command on <paramref name="connection"/> with the configured timeout applied.
    /// </summary>
    /// <param name="connection">The open connection.</param>
    /// <param name="commandText">The command text.</param>
    /// <param name="transaction">The ambient transaction, if any.</param>
    /// <returns>The configured command. The caller owns disposal.</returns>
    public SqlCommand CreateCommand(
        SqlConnection connection, string commandText, SqlTransaction? transaction = null)
    {
        ArgumentNullException.ThrowIfNull(connection);

        SqlCommand command = connection.CreateCommand();
        command.CommandText = commandText;
        command.CommandTimeout = CommandTimeoutSeconds;
        command.Transaction = transaction;
        return command;
    }
}
