using System.Diagnostics.CodeAnalysis;
using Connected.Annotations;
using Connected.Services;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

public partial class Picker<T> : FormComponent<T, string>
{
   protected IKeyInterceptor _keyInterceptor;

   public Picker() : base(new Converter<T, string>()) { }
   protected Picker(Converter<T, string> converter) : base(converter) { }

   [Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }

   private string _elementId = "picker" + Guid.NewGuid().ToString().Substring(0, 8);

   [Inject] private IBrowserWindowSizeProvider WindowSizeListener { get; set; }

   protected string PickerClass =>
      new CssBuilder("picker")
         .AddClass($"picker-inline", PickerVariant != PickerVariant.Static)
         .AddClass($"picker-static", PickerVariant == PickerVariant.Static)
         .AddClass($"rounded", PickerVariant == PickerVariant.Static && !_pickerSquare)
         .AddClass($"elevation-{_pickerElevation}", PickerVariant == PickerVariant.Static)
         .AddClass($"picker-input-button", !Editable && PickerVariant != PickerVariant.Static)
         .AddClass($"picker-input-text", Editable && PickerVariant != PickerVariant.Static)
         .AddClass($"disabled", Disabled && PickerVariant != PickerVariant.Static)
         .AddClass(AdditionalClassList)
         .Build();

   protected string PickerPaperClass =>
      new CssBuilder("picker")
         .AddClass("picker-paper")
         .AddClass("picker-view", PickerVariant == PickerVariant.Inline)
         .AddClass("picker-open", IsOpen && PickerVariant == PickerVariant.Inline)
         .AddClass("picker-popover-paper", PickerVariant == PickerVariant.Inline)
         .AddClass("dialog", PickerVariant == PickerVariant.Dialog)
         .Build();

   protected string PickerInlineClass =>
      new CssBuilder("picker-inline-paper")
         .Build();

   protected string PickerContainerClass =>
      new CssBuilder("picker-container")
         .AddClass("paper-square", _pickerSquare)
         .AddClass("picker-container-landscape",
            Orientation == Orientation.Landscape && PickerVariant == PickerVariant.Static)
         .Build();

   protected string PickerInputClass =>
      new CssBuilder("input-input-control").AddClass(AdditionalClassList)
         .Build();

   protected string ActionClass => new CssBuilder("picker-actions")
      .AddClass(ClassActions)
      .Build();

   /// <summary>
   /// Sets the icon of the input text field
   /// </summary>
   [ExcludeFromCodeCoverage]
   [Parameter]
   [Obsolete("Use AdornmentIcon instead.", true)]
   public string InputIcon
   {
      get { return AdornmentIcon; }
      set { AdornmentIcon = value; }
   }

   /// <summary>
   /// The color of the adornment if used. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Appearance)]
   public ThemeColor AdornmentColor { get; set; } = ThemeColor.Default;

   /// <summary>
   /// Sets the icon of the input text field
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public string AdornmentIcon { get; set; } = Icons.Material.Filled.Event;

   /// <summary>
   /// Sets the aria-label of the input text field icon
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Appearance)]
   public string AdornmentAriaLabel { get; set; } = string.Empty;

   /// <summary>
   /// The short hint displayed in the input before the user enters a value.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public string Placeholder { get; set; }

   /// <summary>
   /// Fired when the dropdown / dialog opens
   /// </summary>
   [Parameter]
   public EventCallback PickerOpened { get; set; }

   /// <summary>
   /// Fired when the dropdown / dialog closes
   /// </summary>
   [Parameter]
   public EventCallback PickerClosed { get; set; }

   /// <summary>
   /// The higher the number, the heavier the drop-shadow. 0 for no shadow set to 8 by default in inline mode and 0 in static mode.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public int Elevation { set; get; } = 8;

   /// <summary>
   /// If true, border-radius is set to 0 this is set to true automatically in static mode but can be overridden with Rounded bool.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public bool Square { get; set; }

   /// <summary>
   /// If true, no date or time can be defined.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public bool ReadOnly { get; set; }

   /// <summary>
   /// If true, border-radius is set to theme default when in Static Mode.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public bool Rounded { get; set; }

   /// <summary>
   /// If string has value, HelperText will be applied.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public string HelperText { get; set; }

   /// <summary>
   /// If true, the helper text will only be visible on focus.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public bool HelperTextOnFocus { get; set; }

   /// <summary>
   /// If string has value the label text will be displayed in the input, and scaled down at the top if the input has value.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public string Label { get; set; }

   /// <summary>
   /// Show clear button.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public bool Clearable { get; set; } = false;

   /// <summary>
   /// If true, the picker will be disabled.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public bool Disabled { get; set; }

   /// <summary>
   /// If true, the picker will be editable.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public bool Editable { get; set; } = false;

   /// <summary>
   /// Hide toolbar and show only date/time views.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public bool DisableToolbar { get; set; }

   /// <summary>
   /// User class names for picker's ToolBar, separated by space
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public string ToolBarClass { get; set; }

   /// <summary>
   /// Picker container option
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public PickerVariant PickerVariant { get; set; } = PickerVariant.Inline;

   /// <summary>
   ///  Variant of the text input
   /// </summary>
   [ExcludeFromCodeCoverage]
   [Parameter]
   [Obsolete("Use Variant instead.", true)]
   public Variant InputVariant
   {
      get { return Variant; }
      set { Variant = value; }
   }

   /// <summary>
   /// Variant of the text input
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Appearance)]
   public Variant Variant { get; set; } = Variant.Text;

   /// <summary>
   /// Sets if the icon will be att start or end, set to false to disable.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public Adornment Adornment { get; set; } = Adornment.End;

   /// <summary>
   /// What orientation to render in when in PickerVariant Static Mode.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public Orientation Orientation { get; set; } = Orientation.Portrait;

   /// <summary>
   /// Sets the Glyph Size.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Appearance)]
   public Size IconSize { get; set; } = Size.Medium;

   /// <summary>
   /// The color of the toolbar, selected and active. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public ThemeColor Color { get; set; } = ThemeColor.Primary;

   /// <summary>
   /// Changes the cursor appearance.
   /// </summary>
   [Obsolete("This is enabled now by default when you use Editable=true. You can remove the parameter.", false)]
   [Parameter]
   public bool AllowKeyboardInput { get; set; }

   /// <summary>
   /// Fired when the text changes.
   /// </summary>
   [Parameter]
   public EventCallback<string> TextChanged { get; set; }

   /// <summary>
   /// The currently selected string value (two-way bindable)
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Data)]
   public string Text
   {
      get => _text;
      set => SetTextAsync(value, true).AndForget();
   }

   private string _text;

   /// <summary>
   /// CSS class that will be applied to the action buttons container
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerAppearance)]
   public string ClassActions { get; set; }

   /// <summary>
   /// Define the action buttons here
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.PickerBehavior)]
   public RenderFragment<Picker<T>> PickerActions { get; set; }

   /// <summary>
   ///  Will adjust vertical spacing.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Appearance)]
   public Margin Margin { get; set; } = Margin.None;

   /// <summary>
   /// A mask for structured input of the date (requires Editable to be true).
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.FormComponent.Behavior)]
   public IMask Mask
   {
      get => _mask;
      set => _mask = value;
   }

   /// <summary>
   /// Gets or sets the origin of the popover's anchor. Defaults to <see cref="Origin.TopLeft"/>
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.Popover.Appearance)]
   public Origin AnchorOrigin { get; set; } = Origin.TopLeft;

   /// <summary>
   /// Gets or sets the origin of the popover's transform. Defaults to <see cref="Origin.TopLeft"/>
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.Popover.Appearance)]
   public Origin TransformOrigin { get; set; } = Origin.TopLeft;

   protected IMask _mask = null;

   protected async Task SetTextAsync(string value, bool callback)
   {
      if (_text != value)
      {
         _text = value;
         if (callback)
            await StringValueChanged(_text);
         await TextChanged.InvokeAsync(_text);
      }
   }

   /// <summary>
   /// Value change hook for descendants.
   /// </summary>
   protected virtual Task StringValueChanged(string value)
   {
      return Task.CompletedTask;
   }

   protected bool IsOpen { get; set; }

   public void ToggleOpen()
   {
      if (IsOpen)
         Close();
      else
         Open();
   }

   public void Close(bool submit = true)
   {
      IsOpen = false;

      if (submit)
      {
         Submit();
      }

      OnClosed();
      StateHasChanged();
   }

   public void Open()
   {
      IsOpen = true;
      StateHasChanged();
      OnOpened();
   }

   private void CloseOverlay() => Close(PickerActions == null);

   protected internal virtual void Submit() { }

   public virtual void Clear(bool close = true)
   {
      if (close && PickerVariant != PickerVariant.Static)
      {
         Close(false);
      }
   }

   protected override void ResetValue()
   {
      _inputReference?.Reset();
      base.ResetValue();
   }

   protected internal TextField<string> _inputReference;

   public virtual ValueTask FocusAsync() => _inputReference?.FocusAsync() ?? ValueTask.CompletedTask;

   public virtual ValueTask BlurAsync() => _inputReference?.BlurAsync() ?? ValueTask.CompletedTask;

   public virtual ValueTask SelectAsync() => _inputReference?.SelectAsync() ?? ValueTask.CompletedTask;

   public virtual ValueTask SelectRangeAsync(int pos1, int pos2) =>
      _inputReference?.SelectRangeAsync(pos1, pos2) ?? ValueTask.CompletedTask;

   private bool _pickerSquare;
   private int _pickerElevation;
   private ElementReference _pickerInlineRef;

   protected override void OnInitialized()
   {
      if (PickerVariant == PickerVariant.Static)
      {
         IsOpen = true;
         if (Elevation == 8)
         {
            _pickerElevation = 0;
         }
         else
         {
            _pickerElevation = Elevation;
         }

         if (!Rounded)
         {
            _pickerSquare = true;
         }
      }
      else
      {
         _pickerSquare = Square;
         _pickerElevation = Elevation;
      }

      if (Label == null && For != null)
         Label = For.GetLabelString();
   }

   private async Task EnsureKeyInterceptor()
   {
      if (_keyInterceptor == null)
      {
         _keyInterceptor = _keyInterceptorFactory.Create();

         await _keyInterceptor.Connect(_elementId, new KeyInterceptorOptions()
         {
            //EnableLogging = true,
            TargetClass = "input-slot",
            Keys =
             {
                new KeyOptions { Key = " ", PreventDown = "key+none" },
                new KeyOptions { Key = "ArrowUp", PreventDown = "key+none" },
                new KeyOptions { Key = "ArrowDown", PreventDown = "key+none" },
                new KeyOptions { Key = "Enter", PreventDown = "key+none" },
                new KeyOptions { Key = "NumpadEnter", PreventDown = "key+none" },
                new KeyOptions { Key = "/./", SubscribeDown = true, SubscribeUp = true }, // for our users
                 },
         });
         _keyInterceptor.KeyDown += HandleKeyDown;
      }
   }

   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
      if (firstRender == true)
      {
         await EnsureKeyInterceptor();
      }

      await base.OnAfterRenderAsync(firstRender);
   }

   protected internal void ToggleState()
   {
      if (Disabled)
         return;
      if (IsOpen)
      {
         IsOpen = false;
         OnClosed();
      }
      else
      {
         IsOpen = true;
         OnOpened();
         FocusAsync();
      }
   }

   protected virtual async void OnOpened()
   {
      OnPickerOpened();

      if (PickerVariant == PickerVariant.Inline)
      {
         await _pickerInlineRef.ChangeCssAsync(PickerInlineClass);
      }

      await EnsureKeyInterceptor();
      await _keyInterceptor.UpdateKey(new() { Key = "Escape", StopDown = "key+none" });
   }

   protected virtual async void OnClosed()
   {
      OnPickerClosed();

      await EnsureKeyInterceptor();
      await _keyInterceptor.UpdateKey(new() { Key = "Escape", StopDown = "none" });
   }

   protected virtual void OnPickerOpened()
   {
      PickerOpened.InvokeAsync(this);
   }

   protected virtual void OnPickerClosed()
   {
      PickerClosed.InvokeAsync(this);
   }

   protected internal virtual void HandleKeyDown(KeyboardEventArgs obj)
   {
      if (Disabled || ReadOnly)
         return;
      switch (obj.Key)
      {
         case "Backspace":
            if (obj.CtrlKey == true && obj.ShiftKey == true)
            {
               Clear();
               _value = default(T);
               Reset();
            }

            break;
         case "Escape":
         case "Tab":
            Close(false);
            break;
      }
   }

   protected override void Dispose(bool disposing)
   {
      base.Dispose(disposing);

      if (disposing == true)
      {
         if (_keyInterceptor != null)
         {
            _keyInterceptor.KeyDown -= HandleKeyDown;
            _keyInterceptor.Dispose();
         }

      }
   }

}
