using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

public partial class Input<T> : InputBase<T>
{
   protected string Classname => InputCssHelper.GetClassname(this,
      () => HasNativeHtmlPlaceholder() || !string.IsNullOrEmpty(Text) || Adornment == Adornment.Start || !string.IsNullOrWhiteSpace(Placeholder));

   protected string InputClassname => InputCssHelper.GetInputClassname(this);

   protected string AdornmentClassname => InputCssHelper.GetAdornmentClassname(this);

   protected string ClearButtonClassname =>
            new CssBuilder()
            .AddClass("me-n1", Adornment == Adornment.End && HideSpinButtons == false)
            .AddClass("icon-button-edge-end", Adornment == Adornment.End && HideSpinButtons == true)
            .AddClass("me-6", Adornment != Adornment.End && HideSpinButtons == false)
            .AddClass("icon-button-edge-margin-end", Adornment != Adornment.End && HideSpinButtons == true)
            .Build();

   /// <summary>
   /// Type of the input element. It should be a valid HTML5 input type.
   /// </summary>
   [Parameter] public InputType InputType { get; set; } = InputType.Text;

   internal override InputType GetInputType() => InputType;

   protected string InputTypeString => InputType.ToDescription();

   protected Task OnInput(ChangeEventArgs args)
   {
      if (!Immediate)
         return Task.CompletedTask;
      _isFocused = true;
      return SetTextAsync(args?.Value as string);
   }

   protected async Task OnChange(ChangeEventArgs args)
   {
      _internalText = args?.Value as string;
      await OnInternalInputChanged.InvokeAsync(args);
      if (!Immediate)
      {
         await SetTextAsync(args?.Value as string);
      }
   }

   /// <summary>
   /// Paste hook for descendants.
   /// </summary>
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously

   protected virtual async Task OnPaste(ClipboardEventArgs args)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
   {
      // do nothing
      return;
   }

   /// <summary>
   /// ChildContent of the MudInput will only be displayed if InputType.Hidden and if its not null.
   /// </summary>
   [Parameter] public RenderFragment ChildContent { get; set; }

   public ElementReference ElementReference { get; private set; }
   private ElementReference _elementReference1;

   public override async ValueTask FocusAsync()
   {
      try
      {
         if (InputType == InputType.Hidden && ChildContent != null)
            await _elementReference1.FocusAsync();
         else
            await ElementReference.FocusAsync();
      }
      catch (Exception e)
      {
         Console.WriteLine("Input.FocusAsync: " + e.Message);
      }
   }

   public override ValueTask BlurAsync()
   {
      return ElementReference.BlurAsync();
   }

   public override ValueTask SelectAsync()
   {
      return ElementReference.SelectAsync();
   }

   public override ValueTask SelectRangeAsync(int pos1, int pos2)
   {
      return ElementReference.SelectRangeAsync(pos1, pos2);
   }

   /// <summary>
   /// Invokes the callback when the Up arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
   /// Note: use the optimized control <see cref="NumericField{T}"/> if you need to deal with numbers.
   /// </summary>
   [Parameter] public EventCallback OnIncrement { get; set; }

   /// <summary>
   /// Invokes the callback when the Down arrow button is clicked when the input is set to <see cref="InputType.Number"/>.
   /// Note: use the optimized control <see cref="MudNumericField{T}"/> if you need to deal with numbers.
   /// </summary>
   [Parameter] public EventCallback OnDecrement { get; set; }

   /// <summary>
   /// Hides the spin buttons for <see cref="MudNumericField{T}"/>
   /// </summary>
   [Parameter] public bool HideSpinButtons { get; set; } = true;

   /// <summary>
   /// Show clear button.
   /// </summary>
   [Parameter] public bool Clearable { get; set; } = false;

   /// <summary>
   /// Button click event for clear button. Called after text and value has been cleared.
   /// </summary>
   [Parameter] public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }

   /// <summary>
   /// Mouse wheel event for input.
   /// </summary>
   [Parameter] public EventCallback<WheelEventArgs> OnMouseWheel { get; set; }

   /// <summary>
   /// Custom clear icon.
   /// </summary>
   [Parameter] public string ClearIcon { get; set; } = Icons.Material.Filled.Clear;

   /// <summary>
   /// Custom numeric up icon.
   /// </summary>
   [Parameter] public string NumericUpIcon { get; set; } = Icons.Material.Filled.KeyboardArrowUp;

   /// <summary>
   /// Custom numeric down icon.
   /// </summary>
   [Parameter] public string NumericDownIcon { get; set; } = Icons.Material.Filled.KeyboardArrowDown;

   private Size GetButtonSize() => Margin == Margin.Dense ? Size.Small : Size.Medium;

   private bool _showClearable;

   private void UpdateClearable(object value)
   {
      var showClearable = Clearable && ((value is string stringValue && !string.IsNullOrWhiteSpace(stringValue)) || (value is not string && value is not null));
      if (_showClearable != showClearable)
         _showClearable = showClearable;
   }

   protected override async Task UpdateTextPropertyAsync(bool updateValue)
   {
      await base.UpdateTextPropertyAsync(updateValue);
      if (Clearable)
         UpdateClearable(Text);
   }

   protected override async Task UpdateValuePropertyAsync(bool updateText)
   {
      await base.UpdateValuePropertyAsync(updateText);
      if (Clearable)
         UpdateClearable(Value);
   }

   protected virtual async Task ClearButtonClickHandlerAsync(MouseEventArgs e)
   {
      await SetTextAsync(string.Empty, updateValue: true);
      await ElementReference.FocusAsync();
      await OnClearButtonClick.InvokeAsync(e);
   }

   private string _internalText;

   public override async Task SetParametersAsync(ParameterView parameters)
   {
      await base.SetParametersAsync(parameters);
      //if (!_isFocused || _forceTextUpdate)
      //    _internalText = Text;
      if (RuntimeLocation.IsServerSide && TextUpdateSuppression)
      {
         // Text update suppression, only in BSS (not in WASM).
         // This is a fix for #1012
         if (!_isFocused || _forceTextUpdate)
            _internalText = Text;
      }
      else
      {
         // in WASM (or in BSS with TextUpdateSuppression==false) we always update
         _internalText = Text;
      }
   }

   /// <summary>
   /// Sets the input text from outside programmatically
   /// </summary>
   /// <param name="text"></param>
   /// <returns></returns>
   public Task SetText(string text)
   {
      _internalText = text;
      return SetTextAsync(text);
   }


   // Certain HTML5 inputs (dates and color) have a native placeholder
   private bool HasNativeHtmlPlaceholder()
   {
      return GetInputType() is InputType.Color or InputType.Date or InputType.DateTimeLocal or InputType.Month
         or InputType.Time or InputType.Week;
   }
}

public class InputString : Input<string> { }
