#nullable enable

using System.Data;

namespace MMoneyCoderSharp.Data;

/// <summary>
/// Identifies a legacy data source without coupling the core project to a provider package.
/// </summary>
public enum DataSourceKind
{
    Oracle,
    MariaDb
}

/// <summary>
/// Executes a migrated query. Infrastructure code supplies the provider implementation.
/// </summary>
public interface IDataQueryExecutor
{
    DataTable Execute(DataSourceKind source, string tableName, string query);

    Task<DataTable> ExecuteAsync(
        DataSourceKind source,
        string tableName,
        string query,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Executes an immutable parameterized read query asynchronously.
    /// Existing adapters remain source-compatible, but must opt in before
    /// accepting a spec that contains parameters.
    /// </summary>
    Task<DataTable> ExecuteAsync(
        DataSourceKind source,
        string tableName,
        DataQuerySpec query,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(query);
        query.ValidateFor(source);

        if (query.Parameters.Count > 0)
        {
            throw new NotSupportedException(
                "This query executor does not support parameterized queries.");
        }

        return ExecuteAsync(source, tableName, query.Sql, cancellationToken);
    }
}

/// <summary>
/// Compatibility seam used while the legacy request objects are migrated incrementally.
/// </summary>
public static class DataQueryExecutor
{
    private static IDataQueryExecutor? _current;

    public static bool IsConfigured => Volatile.Read(ref _current) is not null;

    public static IDataQueryExecutor Current => Volatile.Read(ref _current)
        ?? throw new InvalidOperationException(
            "A database query executor has not been configured. " +
            "Configure the infrastructure adapter before requesting live data.");

    public static void Configure(IDataQueryExecutor executor)
    {
        ArgumentNullException.ThrowIfNull(executor);
        Interlocked.Exchange(ref _current, executor);
    }

    public static void Reset() => Interlocked.Exchange(ref _current, null);
}
