#nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Connected.Interop;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace Connected
{
   [ExcludeFromCodeCoverage]
   public static class ElementReferenceExtensions
   {
      private static readonly PropertyInfo? jsRuntimeProperty =
          typeof(WebElementReferenceContext).GetProperty("JSRuntime", BindingFlags.Instance | BindingFlags.NonPublic);

      internal static IJSRuntime? GetJSRuntime(this ElementReference elementReference)
      {
         if (elementReference.Context is not WebElementReferenceContext context)
         {
            return null;
         }

         return (IJSRuntime?)jsRuntimeProperty?.GetValue(context);
      }

      public static ValueTask FocusFirstAsync(this ElementReference elementReference, int skip = 0, int min = 0) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.focusFirst", elementReference, skip, min) ?? ValueTask.CompletedTask;

      public static ValueTask FocusLastAsync(this ElementReference elementReference, int skip = 0, int min = 0) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.focusLast", elementReference, skip, min) ?? ValueTask.CompletedTask;

      public static ValueTask SaveFocusAsync(this ElementReference elementReference) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.saveFocus", elementReference) ?? ValueTask.CompletedTask;

      public static ValueTask RestoreFocusAsync(this ElementReference elementReference) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.restoreFocus", elementReference) ?? ValueTask.CompletedTask;

      public static ValueTask BlurAsync(this ElementReference elementReference) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.blur", elementReference) ?? ValueTask.CompletedTask;

      public static ValueTask SelectAsync(this ElementReference elementReference) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.select", elementReference) ?? ValueTask.CompletedTask;

      public static ValueTask SelectRangeAsync(this ElementReference elementReference, int pos1, int pos2) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.selectRange", elementReference, pos1, pos2) ?? ValueTask.CompletedTask;

      public static ValueTask ChangeCssAsync(this ElementReference elementReference, string css) =>
          elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.changeCss", elementReference, css) ?? ValueTask.CompletedTask;

      public static ValueTask<BoundingClientRect> MudGetBoundingClientRectAsync(this ElementReference elementReference) =>
          elementReference.GetJSRuntime()?.InvokeAsync<BoundingClientRect>("elementRef.getBoundingClientRect", elementReference) ?? ValueTask.FromResult(new BoundingClientRect());

      public static ValueTask<int[]> AddDefaultPreventingHandlers(this ElementReference elementReference, string[] eventNames) =>
          elementReference.GetJSRuntime()?.InvokeAsync<int[]>("elementRef.addDefaultPreventingHandlers", elementReference, eventNames) ?? new ValueTask<int[]>(Array.Empty<int>());

      public static ValueTask RemoveDefaultPreventingHandlers(this ElementReference elementReference, string[] eventNames, int[] listenerIds)
      {
         if (eventNames.Length != listenerIds.Length)
         {
            throw new ArgumentException($"Number of elements in {nameof(eventNames)} and {nameof(listenerIds)} has to match.");
         }

         return elementReference.GetJSRuntime()?.InvokeVoidAsync("elementRef.removeDefaultPreventingHandlers", elementReference, eventNames, listenerIds) ?? ValueTask.CompletedTask;
      }
   }
}
