using System.Diagnostics.CodeAnalysis;
using Connected.Extensions;

namespace Connected.Components;

public abstract class TableContext
{
   public TableBase Table { get; set; }
   public Action TableStateHasChanged { get; set; }
   public Action PagerStateHasChanged { get; set; }
   public bool HasPager { get; set; }
   public abstract void Add(Tr row, object item);
   public abstract void Remove(Tr row, object item);
   public abstract void UpdateRowCheckBoxes(bool notify = true);
   public List<THeadRow> HeaderRows { get; set; } = new List<THeadRow>();
   public List<TFootRow> FooterRows { get; set; } = new List<TFootRow>();

   public abstract void InitializeSorting();

   public abstract string SortFieldLabel { get; internal set; }

   public abstract SortDirection SortDirection { get; protected set; }

   public abstract void ManagePreviousEditedRow(Tr row);
}

public class TableContext<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T> : TableContext
{
   private Tr editedRow;

   public IEqualityComparer<T> Comparer //when the comparer value is setup, update the collections with the new comparer
   {
      get => _comparer;
      set
      {
         _comparer = value;
         Selection = new HashSet<T>(Selection, _comparer);
         Rows = new Dictionary<T, Tr>(Rows, _comparer);
      }
   }
   private IEqualityComparer<T> _comparer;

   public HashSet<T> Selection { get; set; } = new HashSet<T>();
   public Dictionary<T, Tr> Rows { get; set; } = new Dictionary<T, Tr>();
   public List<TableGroupRow<T>> GroupRows { get; set; } = new List<TableGroupRow<T>>();

   public List<TableSortLabel<T>> SortLabels { get; set; } = new List<TableSortLabel<T>>();

   public override void UpdateRowCheckBoxes(bool notify = true)
   {
      if (!Table.MultiSelection)
         return;
      // update row checkboxes
      foreach (var pair in Rows.ToArray())
      {
         var row = pair.Value;
         var item = pair.Key;
         row.SetChecked(Selection.Contains(item), notify: notify);
      }
      //update group checkboxes
      foreach (var row in GroupRows)
      {
         var rowGroupItems = row.Items.ToList();
         row.SetChecked(Selection.Intersect(rowGroupItems).Count() == rowGroupItems.Count, notify: false);
      }
      if (HeaderRows.Count > 0 || FooterRows.Count > 0)
      {
         var itemsCount = Table.GetFilteredItemsCount();
         var b = Selection.Count == itemsCount && itemsCount != 0;
         // update header checkbox
         foreach (var header in HeaderRows)
            header.SetChecked(b, notify: false);

         // update footer checkbox
         foreach (var footer in FooterRows)
            footer.SetChecked(b, notify: false);
      }
   }

   public override void ManagePreviousEditedRow(Tr row)
   {
      if (Table.IsEditable)
      {
         // Reset edition values of the edited row
         // if another row is selected for edition
         if (editedRow != null && row != editedRow)
         {
            editedRow.ManagePreviousEdition();
         }

         // The selected row is the edited row
         editedRow = row;
      }
   }

   public override void Add(Tr row, object item)
   {
      var t = item.As<T>();
      if (t is null)
         return;
      Rows[t] = row;
   }

   public override void Remove(Tr row, object item)
   {
      var t = item.As<T>();
      if (t is null)
         return;
      if (Rows[t] == row)
         Rows.Remove(t);
      if (!Table.ContainsItem(item))
      {
         Selection.Remove(t);
         Table.UpdateSelection();
      }
   }

   #region --> Sorting

   public override SortDirection SortDirection
   {
      get;
      protected set;
   }

   public Func<T, object> SortBy { get; protected set; }
   public TableSortLabel<T> CurrentSortLabel { get; protected set; }

   public async Task SetSortFunc(TableSortLabel<T> label, bool override_direction_none = false)
   {
      CurrentSortLabel = label;
      if (label.SortDirection == SortDirection.None && override_direction_none)
         label.SetSortDirection(SortDirection.Ascending);
      SortDirection = label.SortDirection;
      SortBy = label.SortBy;
      UpdateSortLabels(label);

      if (Table.HasServerData)
         await Table.InvokeServerLoadFunc();

      TableStateHasChanged();
   }

   public IEnumerable<T> Sort(IEnumerable<T> items)
   {
      if (items == null)
         return items;
      if (SortBy == null || SortDirection == SortDirection.None)
         return items;
      if (SortDirection == SortDirection.Ascending)
         return items.OrderBy(item => SortBy(item));
      else
         return items.OrderByDescending(item => SortBy(item));
   }

   public override void InitializeSorting()
   {
      var initial_sortlabel = SortLabels.FirstOrDefault(x => x.InitialDirection != SortDirection.None);
      if (initial_sortlabel == null)
         return;
      CurrentSortLabel = initial_sortlabel;
      UpdateSortLabels(initial_sortlabel);
      // this will trigger initial sorting of the table
      initial_sortlabel.SetSortDirection(initial_sortlabel.InitialDirection);
      SortDirection = initial_sortlabel.SortDirection;
      SortBy = initial_sortlabel.SortBy;
      TableStateHasChanged();
   }

   private void UpdateSortLabels(TableSortLabel<T> label)
   {
      foreach (var x in SortLabels)
      {
         if (x != label)
            x.SetSortDirection(SortDirection.None);
      }
   }

   public override string SortFieldLabel { get; internal set; }

   #endregion

}
