using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using Microsoft.AspNetCore.Components;

using CommonHelpers.WASM;
using AFXUI;

namespace BookRegistry.Client {

//**************************************************************************************************
partial class XServiceBase {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static UserAuthorizationService UA => Services.UserAuthorizationService;
//==================================================================================================

public static readonly Guid
   Status_SessionExpired = new Guid( "ED8ECB3D-4A8F-49EB-AF38-15A765DF107E" );

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

static TimeSpan AccessTokenExpirationForestalling =>
   new TimeSpan( 0, 1, 0 ); // minutes
static TimeSpan RefreshTokenExpirationForestalling =>
   new TimeSpan( 0, 30, 0 ); // minutes

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="OperationFailedException" />
async Task<ServiceInvocationResult<R>> ESC_InvokeImplementation_A<R>(
   bool alterActionName, string methodName, [HOptional] object? argumentsObj )
where R: GeneralApiResponse
{
   if ( this is UserAuthorizationService )
      throw new InvalidOperationException(
         "Method \"ESC_InvokeImplementation_A\" must not be called on instance of "+
         "\"UserAuthorizationService\"-class." );
   var rtype = typeof(R);
   ConstructorInfo? ctorInfo;
   try {
      ctorInfo = rtype.GetConstructor( new[]{ typeof(GeneralApiResponse) } );
      if ( ctorInfo == null )
         throw new OperationFailedException(
            "Reflection method \"Type.GetMethod\" has unexpectedly returned null." );
   } catch (Exception e) {
      throw OpFailedExceptionCreator.Create(
         "Can not obtain constructor from \"{0}\"-type with \"GeneralApiResponse\" type "+
         "argument.\n"+
         "{1}", rtype, HException.GetErrorMessage(e) );
   }
   if ( Session.IsOpen )
   {
      var nowUtc = DateTime.UtcNow;
      if ( nowUtc.Add( AccessTokenExpirationForestalling ) >= UA.AccessToken.NN().ExpirationUtc )
      {
         var refreshToken = UA.RefreshToken.NN();
         if ( nowUtc >= refreshToken.ExpirationUtc )
         {
            var grsp = new GeneralApiResponse(HLocal.TEXT(
               "User session is expired.\n"+
               "Reopen your session manually in order to renew interaction with the back-end.",
               "Пользовательская сессия истекла.\n"+
               "Переоткройте Вашу сессию вручную, чтобы возобновить взаимодействие с бэкенд-ом." ),
               Status_SessionExpired );
            var response = (R)ctorInfo.Invoke( new object[]{ grsp } );
            return new ServiceInvocationResult<R>( response );
         }
         bool generateBothTokens = nowUtc.Add( RefreshTokenExpirationForestalling ) >=
            refreshToken.ExpirationUtc;
         DebugConsole.PrintOperation( generateBothTokens ?
            "Updating tokens (access & refresh)" :
            "Updating access token" );
         try
         {
            AssignAuthorizationHeader( refreshToken );
            var sir = Session.UserInfo.IsAdministrator ?
               await UA.ESC_RefreshTokens_Admin_A( generateBothTokens ) :
               await UA.ESC_RefreshTokens_General_A( generateBothTokens );
            if ( sir.Exception != null )
               return new ServiceInvocationResult<R>( sir.Exception );
            if ( sir.HasError )
            {
               DebugConsole.PrintError( sir.ErrorMessageNN );
               var response = (R)ctorInfo.Invoke( new object[]{ sir.ResponseNN } );
               return new ServiceInvocationResult<R>( response );
            }
         }
         catch (Exception e)
         {
            DebugConsole.PrintError( e );
            ResetAuthorizationHeader();
            throw;
         }
         DebugConsole.Print( "Update of token(s) has completed." );
      }
   }
   return await ServiceInvocation.ESC_Invoke_A<R>(
      AddressOf( alterActionName, methodName ), argumentsObj );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // XServiceBase
//**************************************************************************************************

} // BookRegistry.Client
