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

namespace DataHelpers.SQL {

/// <summary>
/// </summary>
[h_db_orm_type][u.async_local][u.sync_only_disposable]
public partial class HSP: HQueryExecContextBase {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="InvalidOperationException" />
public HSP( string procedureName )
: base( procedureName )
{}

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

[u.volatile_access] public static string StatusParameterName  => _StatusParameterName_;
[u.volatile_access] public static string MessageParameterName => _MessageParameterName_;

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

/// <exception cref="InvalidOperationException" />
[u.async_local]
public static new string ProcedureName => HQueryExecContextBase.StrictGetProcedureName();

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="InvalidCastException" />
/// <exception cref="HDatabaseException" />
[u.opt_result]
public static T GetOutputParameterValue<T>( SqlParameter parameter,
                                            bool treatNullAsDefaultValue=false ) =>
   HDB.GetOutputParameterValue<T>( ProcedureName, parameter, treatNullAsDefaultValue );

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

[u.async_local]
public static ( HSPResultParameters ResultParameters, SqlCommand Command )
   PrepareCommand()
{
   var Command = HDB.CreateSPCommandEx( Connection, OuterTransaction, ProcedureName );
   var ResultParameters = AddResultParameters( Command );
   return ( ResultParameters, Command );
}
[u.async_local]
public static ( HSPResultParameters ResultParameters, SqlCommand Command )
   PrepareCommand( params SqlParameter[] sqlParams )
{
   var Command = HDB.CreateSPCommandEx( Connection, OuterTransaction, ProcedureName, sqlParams );
   var ResultParameters = AddResultParameters( Command );
   return ( ResultParameters, Command );
}

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

public static HSPResultParameters AddResultParameters( SqlCommand command ) =>
   new HSPResultParameters(
      HSqlParam.AddRetValue( command, SqlDbType.Int, 4 ), // size=4 is not required (may be omitted)
      HSqlParam.AddNullableOut( command, StatusParameterName, SqlDbType.UniqueIdentifier ),
      HSqlParam.AddNullableOut( command, MessageParameterName, SqlDbType.NVarChar, -1 ) );

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

/// <exception cref="InvalidCastException" />
[u.opt_result]
public static int RetValue( HSPResultParameters resultParameters ) =>
   HDB.GetSPReturnValue( ProcedureName, resultParameters.RetValue );
/// <exception cref="InvalidCastException" />
[u.opt_result]
public static Guid Status( HSPResultParameters resultParameters )
{
   object value = resultParameters.Status.Value;
   return value==null || value is DBNull ? Guid.Empty : (Guid)value;
}
public static string? OutMessage( HSPResultParameters resultParameters )
{
   string? value = resultParameters.OutMessage.Value as string;
   if ( value is null )
      return null;
   value = value.Trim();
   return value != "" ? value : u.local.T(
      "Empty message has been returned by database query.",
      "Пустое сообщение было возвращено запросом к БД." );
}

/// <exception cref="InvalidCastException" />
public static bool Success( HSPResultParameters resultParameters ) =>
   RetValue( resultParameters ) == 0;
/// <exception cref="InvalidCastException" />
/// <exception cref="HDatabaseException" />
public static void VerifySuccess( HSPResultParameters resultParameters )
{
   if ( !Success( resultParameters ) )
      throw HDBExceptionCreator.New(u.local.T(
         "Stored procedure [{0}] has unexpectedly returned non-successful result.",
         "Хранимая процедура [{0}] неожиданно вернула неуспешный результат." ),
         ProcedureName );
}

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

/// <exception cref="InvalidCastException" />
public static bool HasStatus( HSPResultParameters resultParameters ) =>
   Status( resultParameters ) != Guid.Empty;

/// <exception cref="InvalidCastException" />
/// <exception cref="HDatabaseException" />
public static void VerifyStatusEmpty( HSPResultParameters resultParameters )
{
   if ( HasStatus( resultParameters ) )
      throw HDBExceptionCreator.New(u.local.T(
         "Stored procedure [{0}] has unexpectedly returned non-empty status.",
         "Хранимая процедура [{0}] неожиданно вернула непустой статус." ),
         ProcedureName );
}

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

/// <exception cref="InvalidCastException" />
/// <exception cref="HDatabaseException" />
public static void VerifyResultParameters( HSPResultParameters resultParameters )
{
   try {
      VerifySuccess( resultParameters );
   }
   catch ( HDatabaseException e ) {
      var outMessage = OutMessage( resultParameters );
      if ( outMessage == null )
         throw;
      throw new HDatabaseException( $"{e.Message}\r\n{outMessage}" );
   }
}

/// <exception cref="InvalidCastException" />
/// <exception cref="HDatabaseException" />
static void VerifyResultParamsIfNotNull( HSPResultParameters? resultParameters )
{
   if ( resultParameters != null )
      HSP.VerifyResultParameters( resultParameters );
}

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

[u.thread_safe]
public static void SpecifyStatusParameterName( string value ) =>
   _StatusParameterName_ = value;
[u.thread_safe]
public static void SpecifyMessageParameterName( string value ) =>
   _MessageParameterName_ = value;

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

/// <exception cref="HDatabaseException" />
[u.async_local]
public static List<RSet_Item> VerifyRSet<RSet_Item>( List<RSet_Item>? list ) =>
   list ?? throw HDBExceptionCreator.New(u.local.T(
      "Stored procedure [{0}] has not generetad expected result set.",
      "Хранимая процедура [{0}] не сгенерировала ожидаемый резалт-сет." ),
      ProcedureName );
/// <exception cref="HDatabaseException" />
[u.async_local]
public static List<RSet_Item> VerifyRSet<RSet_Item>( List<RSet_Item>? list,
                                                     [u.positive] int resultSetNo )
{
   u.arg_check.Positive( resultSetNo, nameof(resultSetNo) );
   return list ?? throw new HDatabaseException(HSP.AppendProcNameToMessage(
      u.local.F(
         "Database query has not returned expected result set #{0}.",
         "Запрос к базе данных не вернул ожидаемый резалт-сет #{0}.",
         resultSetNo ) ));
}
/// <exception cref="HDatabaseException" />
[u.async_local]
public static TOutData VerifyOutData<TOutData>( TOutData? output )
where TOutData: class =>
   output ?? throw HDBExceptionCreator.New(u.local.T(
      "Stored procedure [{0}] has not generetad expected output data.",
      "Хранимая процедура [{0}] не сгенерировала ожидаемые выходные данные." ),
      ProcedureName );

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

[u.async_local]
static SqlTransaction? OuterTransaction =>
   HOuterConnection.Current?.Transaction;

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

static volatile string
   _StatusParameterName_ = "RStatus",
   _MessageParameterName_ = "RMessage";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HSP

} // DataHelpers.SQL
