using System.Data;
using System.Data.Common;
using System.Runtime.CompilerServices;
using Dapper;
using FruityFoundation.DataAccess.Abstractions;

namespace FruityFoundation.DataAccess.Core;

// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
public class NonTransactionalDbConnection<TConnectionType> : INonTransactionalDbConnection<TConnectionType>
	where TConnectionType : ConnectionType
{
	private readonly DbConnection _connection;

	/// <summary>
	/// C'tor
	/// </summary>
	public NonTransactionalDbConnection(DbConnection connection)
	{
		_connection = connection;
	}

	/// <inheritdoc />
	public async Task<IEnumerable<T>> Query<T>(
		string sql,
		object? param = null,
		CancellationToken cancellationToken = default
	) => await _connection.QueryAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));

	/// <inheritdoc />
	public async IAsyncEnumerable<T> QueryUnbuffered<T>(
		string sql,
		object? param = null,
		[EnumeratorCancellation] CancellationToken cancellationToken = default
	)
	{
		var query = _connection.QueryUnbufferedAsync<T>(sql, param, transaction: null)
			.WithCancellation(cancellationToken);

		await foreach (var item in query)
			yield return item;
	}

	/// <inheritdoc />
	public async Task<T> QuerySingle<T>(
		string sql,
		object? param = null,
		CancellationToken cancellationToken = default
	) => await _connection.QuerySingleAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));

	/// <inheritdoc />
	public async Task Execute(
		string sql,
		object? param = null,
		CancellationToken cancellationToken = default
	) => await _connection.ExecuteAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));

	/// <inheritdoc />
	public async Task<T?> ExecuteScalar<T>(
		string sql,
		object? param = null,
		CancellationToken cancellationToken = default
	) => await _connection.ExecuteScalarAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));

	/// <inheritdoc />
	public async Task<DbDataReader> ExecuteReader(
		string sql,
		object? param = null,
		CancellationToken cancellationToken = default
	) => await _connection.ExecuteReaderAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));

	/// <inheritdoc />
	public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(CancellationToken cancellationToken)
	{
		if (!_connection.State.HasFlag(ConnectionState.Open))
			await _connection.OpenAsync(cancellationToken);

		var tx = await _connection.BeginTransactionAsync(cancellationToken);

		return new DbTransaction<TConnectionType>(tx);
	}

	/// <inheritdoc />
	public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel, CancellationToken cancellationToken)
	{
		if (!_connection.State.HasFlag(ConnectionState.Open))
			await _connection.OpenAsync(cancellationToken);

		var tx = await _connection.BeginTransactionAsync(isolationLevel, cancellationToken);

		return new DbTransaction<TConnectionType>(tx);
	}

	protected virtual void Dispose(bool disposing)
	{
		if (disposing)
		{
#pragma warning disable IDISP007
			_connection.Dispose();
#pragma warning restore IDISP007
		}
	}

	/// <inheritdoc />
	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	protected virtual async ValueTask DisposeAsyncCore()
	{
#pragma warning disable IDISP007
		await _connection.DisposeAsync();
#pragma warning restore IDISP007
	}

	/// <inheritdoc />
	public async ValueTask DisposeAsync()
	{
		await DisposeAsyncCore();
		GC.SuppressFinalize(this);
	}
}