//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// AFXUI auxiliary module for Blazor-application (WASM) | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

using CommonHelpers.WASM;

namespace AFXUI {

//**************************************************************************************************
public static partial class UBreakdown {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static NavigationManager NAV => ServicesAFX.NavigationManager;
//static IJSRuntime JSRT => ServicesAFX.JSRuntime;
//==================================================================================================

public static string? ExceptionCause { get; private set; }
public static string? ErrorMessage { get; private set; }
public static Exception? Exception { get; private set; }
public static string? ExceptionHtml { get; private set; }

public static bool HasErrorInfo =>
   ExceptionCause!=null || ErrorMessage!=null || Exception!=null;

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

public static void ResetErrorInfo()
{
   ExceptionCause = null;
   ErrorMessage = null;
   Exception = null;
   ExceptionHtml = null;
}

public static void NotifyErrorShown() =>
   _ErrorNotShownYet_ = _IgnorePopupDialog_ = false;

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

[HReturnsFalse]
public static bool ProcessError( string message ) =>
   DoProcessError( message, null );
[HReturnsFalse]
public static bool ProcessErrorFmt( string messageFmt, params object?[] parameters ) =>
   ProcessError( HString.FormatAsText( messageFmt, parameters ) );

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

[HReturnsFalse]
public static bool ProcessUnhandledException( Exception e ) =>
   DoProcessError( UnhandledExceptionCause, e );
[HReturnsFalse]
public static bool ProcessUnhandledException( string? errorCause, Exception e ) =>
   DoProcessError( errorCause ?? UnhandledExceptionCause, e );

[HReturnsFalse]
public static bool ProcessJSInvocationError( Exception e ) =>
   ProcessUnhandledException( JSInvocationErrorCause, e );

/// <exception cref="Exception" />
public static void ProcessRenderingError( Type componentType, Exception e,
                                          bool navigateToErrorPage = true ) =>
   DoProcessError( GetRenderingErrorCause( componentType ), e, true, navigateToErrorPage );

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

public static bool Do(Func<Task> body)
{
   try { body(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return false; }
   return true;
}

public static R? ReturnRef<R>(Func<R> body) where R: class
{
   try { return body(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}
public static R? ReturnVal<R>(Func<R> body) where R: struct
{
   try { return body(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}

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

public static async Task<bool> Do_A(Func<Task> bodyA)
{
   try { await bodyA(); }
   catch (Exception e) {
      return ProcessUnhandledException(e); }
   return true;
}

public static async Task<R?> ReturnRef_A<R>(Func<Task<R>> bodyA) where R: class
{
   try { return await bodyA(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}
public static async Task<R?> ReturnVal_A<R>(Func<Task<R>> bodyA) where R: struct
{
   try { return await bodyA(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}

public static async ValueTask<R?> ReturnRefVT_A<R>(Func<ValueTask<R>> bodyA)
where R: class
{
   try { return await bodyA(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}
public static async ValueTask<R?> ReturnValVT_A<R>(Func<ValueTask<R>> bodyA)
where R: struct
{
   try { return await bodyA(); }
   catch (Exception e) {
      ProcessUnhandledException(e);
      return null; }
}

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

internal static string ErrorMessageOfUndefined(string? message) =>
   message ?? "Undefined error in SPA.";

internal static string ComposeErrorMessage(string? exceptionCause,Exception e,bool showDetails=false)
{
   var sb = new StringBuilder();
   sb.AppendLine( exceptionCause ?? UnhandledExceptionCause );
   sb.Append( showDetails ?
      HException.ComposeChainedMessage( e, showDetails ) :
      HException.GetErrorMessage( e ) );
   return sb.ToString();
}

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

[HReturnsFalse]
static bool DoProcessError( string? errorCauseOrErrorMessage, Exception? e,
                            bool isRenderingError = false,
                            bool navigateToErrorPage = true )
{
   if ( UseFatalBreakdownInstead )
      return UFatalBreakdown.DoProcessError( errorCauseOrErrorMessage, e, isRenderingError );
   try
   {
      string debugMessage = e != null ?
         ComposeErrorMessage( errorCauseOrErrorMessage, e, UProgram.IsDebugConfiguration ) :
         ErrorMessageOfUndefined( errorCauseOrErrorMessage );
      try { UConsole.PrintError( debugMessage ); }
      catch { Console.WriteLine( debugMessage ); }
      string popupMessage = e != null ?
         ComposeErrorMessage( errorCauseOrErrorMessage, e ) :
         ErrorMessageOfUndefined( errorCauseOrErrorMessage );
      if ( !_IgnorePopupDialog_ )
      {
         _IgnorePopupDialog_ = true;
         ResetErrorInfo();
         if ( e != null )
         {
            ExceptionCause = errorCauseOrErrorMessage;
            Exception = e;
            ExceptionHtml = PrepareExceptionHtml( e );
         }
         else
            ErrorMessage = errorCauseOrErrorMessage;
         _ErrorNotShownYet_ = true;
         UJS.Invoke( "Breakdown_ShowWasmError", popupMessage, !navigateToErrorPage );
         if ( navigateToErrorPage )
            UBackwardDeferredAFX.Invoke( "wasm_Breakdown_NavigateToErrorPage" );
      }
   }
   catch
   {
      throw HFatalError.Raise( ExceptionInBreakdownCause, e );
   }
   if ( isRenderingError && e!=null )
      throw e;
   return false;
}

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

[JSInvokable]
public static void wasm_Breakdown_ResetIgnorePopupDialog()
{
   if ( !_ErrorNotShownYet_ )
      _IgnorePopupDialog_ = false;
}
[JSInvokable]
public static bool wasm_Breakdown_UseFatalBreakdownInstead() =>
   UseFatalBreakdownInstead;
[JSInvokable]
public static void wasm_Breakdown_NavigateToErrorPage() =>
   NavigateToErrorPage();
[JSInvokable]
public static void wasm_Breakdown_NavigateToErrorPageFromScripError(string scriptErrorMessage)
{
   ResetErrorInfo();
   ExceptionCause = "Script error has occured in JS-code.";
   ErrorMessage = scriptErrorMessage ?? "Undefined script error";
   NavigateToErrorPage();
}

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

static string ApplicaitonName => UProgram.ApplicationNameOrWASM;
static bool UseFatalBreakdownInstead => UFatalBreakdown.IsInFatalError;

public static void NavigateToErrorPage() =>
   NAV.NavigateTo( "/error" );

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

internal static string UnhandledExceptionCause =>
   $"Unhandled exception in \"{ApplicaitonName}\"-app.:";
internal static string ExceptionInBreakdownCause =>
   $"Exception has occured in breakdown of \"{ApplicaitonName}\"-app.:";
internal static string PageScriptErrorCause =>
   $"Page script error in \"{ApplicaitonName}\"-app.:";
internal static string JSInvocationErrorCause =>
   $"Exception has occured in \"{ApplicaitonName}\"-app. during JS-invocation:";
internal static string GetRenderingErrorCause(Type componentType) =>
   $"Exception has occured in \"{ApplicaitonName}\"-app. during rendering in "+
   $"\"{componentType}\"-component:";

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

static bool _ErrorNotShownYet_, _IgnorePopupDialog_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UBreakdown
//**************************************************************************************************

} // AFXUI
