using System.Globalization;
using System.Windows.Input;
using Connected.Annotations;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Connected.Components;

public partial class TreeViewItem<T> : UIComponent
{
   private string _text;
   private bool _disabled;
   private bool _isChecked, _isSelected, _isServerLoaded;
   private ToStringConverter<T> _converter = new DefaultConverter<T>();
   private readonly List<TreeViewItem<T>> _childItems = new();

   protected string Classname =>
   new CssBuilder("treeview-item")
     .AddClass(AdditionalClassList)
   .Build();

   protected string ContentClassname =>
   new CssBuilder("treeview-item-content")
     .AddClass("cursor-pointer", TreeRoot?.IsSelectable == true || TreeRoot?.ExpandOnClick == true && HasChild)
     .AddClass($"treeview-item-selected", _isSelected)
   .Build();

   public string TextClassname =>
   new CssBuilder("treeview-item-label")
      .AddClass(TextClass)
   .Build();


   [CascadingParameter] TreeView<T> TreeRoot { get; set; }

   [CascadingParameter] TreeViewItem<T> Parent { get; set; }

   /// <summary>
   /// Custom checked icon, leave null for default.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Selecting)]
   public string CheckedIcon { get; set; } = Icons.Material.Filled.CheckBox;

   /// <summary>
   /// Custom unchecked icon, leave null for default.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Selecting)]
   public string UncheckedIcon { get; set; } = Icons.Material.Filled.CheckBoxOutlineBlank;

   /// <summary>
   /// Value of the treeviewitem. Acts as the displayed text if no text is set.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Data)]
   public T Value { get; set; }

   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;

   /// <summary>
   /// The text to display
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public string Text
   {
      get => string.IsNullOrEmpty(_text) ? _converter.Convert(Value) : _text;
      set => _text = value;
   }

   /// <summary>
   /// Tyopography for the text.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public Typo TextTypo { get; set; } = Typo.body1;

   /// <summary>
   /// User class names for the text, separated by space.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public string TextClass { get; set; }

   /// <summary>
   /// The text at the end of the item.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public string EndText { get; set; }

   /// <summary>
   /// Tyopography for the endtext.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public Typo EndTextTypo { get; set; } = Typo.body1;

   /// <summary>
   /// User class names for the endtext, separated by space.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public string EndTextClass { get; set; }

   /// <summary>
   /// If true, treeviewitem will be disabled.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public bool Disabled
   {
      get => _disabled || (TreeRoot?.Disabled ?? false);
      set => _disabled = value;
   }

   /// <summary>
   /// Child content of component used to create sub levels.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Data)]
   public RenderFragment ChildContent { get; set; }

   /// <summary>
   /// Content of the item, if used completly replaced the default rendering.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public RenderFragment Content { get; set; }

   [Parameter]
   [Category(CategoryTypes.TreeView.Data)]
   public HashSet<T> Items { get; set; }

   /// <summary>
   /// Command executed when the user clicks on the CommitEdit Button.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.ClickAction)]
   public ICommand Command { get; set; }

   /// <summary>
   /// Expand or collapse treeview item when it has children. Two-way bindable. Note: if you directly set this to
   /// true or false (instead of using two-way binding) it will force the item's expansion state.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Expanding)]
   public bool Expanded { get; set; }

   /// <summary>
   /// Called whenever expanded changed.
   /// </summary>
   [Parameter] public EventCallback<bool> ExpandedChanged { get; set; }

   [Parameter]
   [Category(CategoryTypes.TreeView.Selecting)]
   public bool Activated
   {
      get => _isSelected;
      set
      {
         _ = TreeRoot?.UpdateSelected(this, value);
      }
   }

   [Parameter]
   [Category(CategoryTypes.TreeView.Selecting)]
   public bool Selected
   {
      get => _isChecked;
      set
      {
         if (_isChecked == value)
            return;

         _isChecked = value;
         TreeRoot?.UpdateSelectedItems();
         SelectedChanged.InvokeAsync(_isChecked);
      }
   }

   /// <summary>
   /// Glyph placed before the text if set.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public string Icon { get; set; }

   /// <summary>
   /// The color of the icon. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public ThemeColor IconColor { get; set; } = ThemeColor.Default;

   /// <summary>
   /// Glyph placed after the text if set.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Behavior)]
   public string EndIcon { get; set; }

   /// <summary>
   /// The color of the icon. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public ThemeColor EndIconColor { get; set; } = ThemeColor.Default;

   /// <summary>
   /// The expand/collapse icon.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Expanding)]
   public string ExpandedIcon { get; set; } = Icons.Material.Filled.ChevronRight;

   /// <summary>
   /// The color of the expand/collapse button. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Expanding)]
   public ThemeColor ExpandedIconColor { get; set; } = ThemeColor.Default;

   /// <summary>
   /// The loading icon.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public string LoadingIcon { get; set; } = Icons.Material.Filled.Loop;

   /// <summary>
   /// The color of the loading. It supports the theme colors.
   /// </summary>
   [Parameter]
   [Category(CategoryTypes.TreeView.Appearance)]
   public ThemeColor LoadingIconColor { get; set; } = ThemeColor.Default;

   /// <summary>
   /// Called whenever the activated value changed.
   /// </summary>
   [Parameter] public EventCallback<bool> ActivatedChanged { get; set; }

   /// <summary>
   /// Called whenever the selected value changed.
   /// </summary>
   [Parameter] public EventCallback<bool> SelectedChanged { get; set; }

   /// <summary>
   /// Tree item click event.
   /// </summary>
   [Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }

   /// <summary>
   /// Tree item double click event.
   /// </summary>
   [Parameter] public EventCallback<MouseEventArgs> OnDoubleClick { get; set; }

   public bool Loading { get; set; }

   bool HasChild => ChildContent != null ||
      (TreeRoot != null && Items != null && Items.Count != 0) ||
      (TreeRoot?.ServerData != null && !_isServerLoaded && (Items == null || Items.Count == 0));

   protected bool IsChecked
   {
      get => Selected;
      set { _ = SelectItem(value, this); }
   }

   protected internal bool ArrowExpanded
   {
      get => Expanded;
      set
      {
         if (value == Expanded)
            return;

         Expanded = value;
         ExpandedChanged.InvokeAsync(value);
      }
   }

   protected override void OnInitialized()
   {
      if (Parent != null)
      {
         Parent.AddChild(this);
      }
      else
      {
         TreeRoot?.AddChild(this);
      }
   }

   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
      if (firstRender && _isSelected)
      {
         await TreeRoot.UpdateSelected(this, _isSelected);
      }

      await base.OnAfterRenderAsync(firstRender);
   }

   protected async Task OnItemClicked(MouseEventArgs ev)
   {
      if (TreeRoot?.IsSelectable ?? false)
      {
         await TreeRoot.UpdateSelected(this, !_isSelected);
      }

      if (HasChild && (TreeRoot?.ExpandOnClick ?? false))
      {
         Expanded = !Expanded;
         TryInvokeServerLoadFunc();
         await ExpandedChanged.InvokeAsync(Expanded);
      }

      await OnClick.InvokeAsync(ev);
      if (Command?.CanExecute(Value) ?? false)
      {
         Command.Execute(Value);
      }
   }

   protected async Task OnItemDoubleClicked(MouseEventArgs ev)
   {
      if (TreeRoot?.IsSelectable ?? false)
      {
         await TreeRoot.UpdateSelected(this, !_isSelected);
      }

      if (HasChild && (TreeRoot?.ExpandOnDoubleClick ?? false))
      {
         Expanded = !Expanded;
         TryInvokeServerLoadFunc();
         await ExpandedChanged.InvokeAsync(Expanded);
      }

      await OnDoubleClick.InvokeAsync(ev);
   }

   protected internal Task OnItemExpanded(bool expanded)
   {
      if (Expanded == expanded)
         return Task.CompletedTask;

      Expanded = expanded;
      TryInvokeServerLoadFunc();
      return ExpandedChanged.InvokeAsync(expanded);
   }

   internal Task Select(bool value)
   {
      if (_isSelected == value)
         return Task.CompletedTask;

      _isSelected = value;

      StateHasChanged();

      return ActivatedChanged.InvokeAsync(_isSelected);
   }

   internal async Task SelectItem(bool value, TreeViewItem<T> source = null)
   {
      if (value == _isChecked)
         return;

      _isChecked = value;
      _childItems.ForEach(async c => await c.SelectItem(value, source));

      StateHasChanged();

      await SelectedChanged.InvokeAsync(_isChecked);

      if (source == this)
      {
         if (TreeRoot != null)
         {
            await TreeRoot.UpdateSelectedItems();
         }
      }
   }

   private void AddChild(TreeViewItem<T> item) => _childItems.Add(item);

   internal IEnumerable<TreeViewItem<T>> GetSelectedItems()
   {
      if (_isChecked)
         yield return this;

      foreach (var treeItem in _childItems)
      {
         foreach (var selected in treeItem.GetSelectedItems())
         {
            yield return selected;
         }
      }
   }

   internal async void TryInvokeServerLoadFunc()
   {
      if (Expanded && (Items == null || Items.Count == 0) && TreeRoot?.ServerData != null)
      {
         Loading = true;
         StateHasChanged();

         Items = await TreeRoot.ServerData(Value);

         Loading = false;
         _isServerLoaded = true;

         StateHasChanged();
      }
   }
}
