//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// DataHelpers.SQL satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Data;
using System.Data.SqlClient;
using u = user_rtx;

namespace DataHelpers.SQL {

/// <summary>
/// </summary>
[h_db_orm_type][u.sync_only_disposable]
public partial class HOuterConnection: u.DisposableSyncImpl<HOuterConnection> {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[u.async_local]
public static bool HasCurrentInstance =>
   Current != null;

/// <exception cref="InvalidOperationException" />
[u.async_local]
public static HOuterConnection? Current =>
   GetCurrent();

/// <exception cref="InvalidOperationException" />
[u.async_local][u.strict_result]
public static HOuterConnection CurrentNN =>
   GetCurrent() ?? throw new InvalidOperationException(u.local.T(
      "Outer connection instance is absent (“HOuterConnection”).",
      "Отсутствует экземпляр обрамляющего подключения (“HOuterConnection”)." ));

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public SqlConnection Connection =>
   u.prop_check.Initialized( _connection, GetType() );
/// <exception cref="InvalidOperationException" />
public string ConnectionString { get; }
/// <exception cref="InvalidOperationException" />
public bool IsInTransaction =>
   Transaction != null;
/// <exception cref="InvalidOperationException" />
public SqlTransaction? Transaction =>
   _transaction;
/// <exception cref="InvalidOperationException" />
[u.strict_result]
public SqlTransaction TransactionNN =>
   Transaction ?? throw OpenTransactionWasExpected_Error();

//==================================================================================================

/// <exception cref="InvalidOperationException" />
[MethodImpl( MethodImplOptions.NoInlining )]
public static HOuterConnection Open()
{
   var instance = new HOuterConnection();
   var conn = new SqlConnection( instance.ConnectionString );
   conn.Open();
   instance._connection = conn;
   instance.ValidateDisposable();
   SetCurrent( instance );
   return instance;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction() =>
   OpenTransaction( conn => conn.BeginTransaction() );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( IsolationLevel isolationLevel ) =>
   OpenTransaction( conn => conn.BeginTransaction( isolationLevel ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( string transactionName ) =>
   OpenTransaction( conn => conn.BeginTransaction( transactionName ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( IsolationLevel isolationLevel,
                                                string transactionName ) =>
   OpenTransaction( conn => conn.BeginTransaction( isolationLevel, transactionName ) );

/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
[MethodImpl( MethodImplOptions.NoInlining )]
static HOuterConnection OpenTransaction( Func<SqlConnection,SqlTransaction> beginTransaction )
{
   var instance = new HOuterConnection();
   var conn = new SqlConnection( instance.ConnectionString );
   conn.Open();
   try {
      instance._transaction = beginTransaction( conn );
   } catch {
      u.ops.Try(()=>{ conn.Close(); });
      throw;
   }
   instance._connection = conn;
   instance.ValidateDisposable();
   SetCurrent( instance );
   return instance;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public void VerifyInTransaction()
{
   if ( !IsInTransaction )
      throw OpenTransactionWasExpected_Error();
}
/// <exception cref="InvalidOperationException" />
public void VerifyNotInTransaction()
{
   if ( IsInTransaction )
      throw new InvalidOperationException(u.local.T(
         "Open transaction was not expected.",
         "Не ожидалось наличия открытой транзакции." ));
}

static InvalidOperationException OpenTransactionWasExpected_Error() =>
   new InvalidOperationException(u.local.T(
      "Open transaction was expected.",
      "Ожидалась открытая транзакция." ));

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction() =>
   BeginTransaction( conn => conn.BeginTransaction() );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( IsolationLevel isolationLevel ) =>
   BeginTransaction( conn => conn.BeginTransaction( isolationLevel ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( string transactionName ) =>
   BeginTransaction( conn => conn.BeginTransaction( transactionName ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( IsolationLevel isolationLevel,
                              string transactionName ) =>
   BeginTransaction( conn => conn.BeginTransaction( isolationLevel, transactionName ) );

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
void BeginTransaction( Func<SqlConnection,SqlTransaction> beginTransaction )
{
   VerifyNotInTransaction();
   _transaction = beginTransaction( Connection );
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void CommitTransaction()
{
   var tran = TransactionNN;
   tran.Commit();
   _transaction = null;
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void RollbackTransaction()
{
   VerifyInTransaction();
   TransactionNN.Rollback();
   _transaction = null;
}

//==================================================================================================

/// <exception cref="InvalidOperationException" />
HOuterConnection()
{
   if ( HasCurrentInstance )
      throw new InvalidOperationException(u.local.T(
         "Attempt to repeatedly create “HOuterConnection”-object.",
         "Попытка повторного создания объекта “HOuterConnection”." ));
   var connStrAccessor = HQueryExecContextBase.ConnectionStringAccessor ??
      throw u.e_invalid_operation.New(u.local.T(
         "Property is not assigned: “{0}”.",
         "Свойство не присвоено: “{0}”." ),
         "HQueryExecContextBase.ConnectionStringAccessor" );
   ConnectionString = connStrAccessor();
}

//==================================================================================================

protected override HOuterConnection DisposableSelf => this;

/// <exception cref="InvalidOperationException" />
[u.async_local]
protected override void OnSyncDispose()
{
   if ( _connection == null )
      return;
   SetCurrent( null );
   var conn = u.ops.NullExchange( ref _connection ).NN();
   HQueryExecContextBase.EnsureConnected_UnexpectedConnectionState( conn );
   conn.Close();
}

//==================================================================================================

[u.async_local]
static HOuterConnection? GetCurrent() =>
   _asyncStatic.Value;
[u.async_local]
static void SetCurrent( HOuterConnection? instance ) =>
   _asyncStatic.Value = instance;

//==================================================================================================

static readonly AsyncLocal<HOuterConnection?>
   _asyncStatic = new ();

//--------------------------------------------------------------------------------------------------

SqlConnection? _connection;
SqlTransaction? _transaction;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HOuterConnection

} // DataHelpers.SQL
