/* eslint-disable @typescript-eslint/no-explicit-any */

/* eslint-disable react-refresh/only-export-components */
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
  type CellArray,
  type CellClickedEventArgs,
  CompactSelection,
  type DataEditorProps,
  type DataEditorRef,
  type EditableGridCell,
  type GetCellsThunk,
  type GridCell,
  GridCellKind,
  type GridColumn,
  type GridMouseEventArgs,
  type GridSelection,
  type HeaderClickedEventArgs,
  type Item,
  type Rectangle,
} from '@glideapps/glide-data-grid';
import { IconGrid4x4 } from '@tabler/icons-react';
import { getUUID } from '@warkypublic/artemis-kit';
import { getNestedValue } from '@warkypublic/artemis-kit/object';
import { createSyncStore } from '@warkypublic/zustandsyncstore';
import { produce } from 'immer';
import { type PropsWithChildren, type ReactNode, useEffect } from 'react';

import {
  type MantineBetterMenuInstance,
  type MantineBetterMenuInstanceItem,
  useMantineBetterMenus,
} from '../../MantineBetterMenu';
import { ColumnFilterSet, type GridlerColumn, type GridlerColumns } from './Column';
import { SortDownSprite } from './sprites/SortDown';
import { SortUpSprite } from './sprites/SortUp';
import { SpriteImage } from './sprites/SpriteImage';

export type FilterOption = {
  datatype?: 'array' | 'boolean' | 'date' | 'function' | 'number' | 'object' | 'string';
  id: string;
  operator: FilterOptionOperator;
  value: string;
  value2?: string;
};
export type FilterOptionOperator =
  | 'between'
  | 'contains'
  | 'endswith'
  | 'eq'
  | 'gt'
  | 'gte'
  | 'lt'
  | 'lte'
  | 'neq'
  | 'startswith';

export interface GridlerProps extends PropsWithChildren {
  allowMultiSelect?: boolean;
  columns?: GridlerColumns;

  defaultSort?: Array<SortOption>;
  enableOddEvenRowColor?: boolean;
  getMenuItems?: (
    id: string,
    storeState: GridlerState,
    row?: any,
    col?: GridlerColumn,
    defaultItems?: Array<MantineBetterMenuInstanceItem>
  ) => Array<MantineBetterMenuInstanceItem>;

  glideProps?: Partial<DataEditorProps>;
  headerHeight?: number;
  height?: number | string;
  hideMenu?: (id: string) => void;
  keyField?: string;
  maxConcurrency?: number;
  onChange?: (values: Array<Record<string, any>>) => void;
  onMounted?: (getState: GridlerState['getState'], setState: GridlerState['setState']) => void;
  onUnMounted?: () => void;
  pageSize?: number;
  progressiveScroll?: boolean;
  RenderCell?: <TRowType extends Record<string, string>>(
    row: TRowType,
    colindex: number,
    colid: string,
    value: any,
    store: GridlerState
  ) => GridCell;

  rowHeight?: number;
  scrollToRowKey?: number;
  searchFields?: Array<string>;
  searchStr?: string;
  sections?: {
    bottom?: React.ReactNode;
    left?: React.ReactNode;
    right?: React.ReactNode;
    rightElementDisabled?: boolean;
    rightElementEnd?: React.ReactNode;
    rightElementStart?: React.ReactNode;
    top?: React.ReactNode;
  };
  selectedRowKey?: number;
  selectFirstRowOnMount?: boolean;
  selectMode?: 'cell' | 'row';
  showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
  title?: string;
  tooltipBarProps?: React.HTMLAttributes<HTMLDivElement>;
  total_rows?: number;

  uniqueid: string;
  values?: Array<Record<string, any>>;
  width?: number | string;
}

export interface GridlerRef {
  getGlideRef: () => DataEditorRef | undefined;
  getState: GridlerState['getState'];
  isEmpty: () => boolean;
  refresh: (parms?: any) => Promise<void>;
  reload: (parms?: any) => Promise<void>;
  reloadRow: (key: number | string) => Promise<void>;
  scrollToRow: (key: number | string) => Promise<void>;
  selectRow: (key: number | string) => Promise<void>;
  setStateFN: GridlerState['setStateFN'];
}

export interface GridlerState {
  _active_requests?: Array<{ controller: AbortController; page: number }>;
  _activeTooltip?: ReactNode;
  _events: EventTarget;
  _glideref?: DataEditorRef;
  _gridSelection?: GridSelection;
  _gridSelectionRows?: GridSelection['rows'];
  _loadingList: CompactSelection;
  _page_data: Record<number, Array<any>>;
  _refresh: () => Promise<void>;
  _scrollTimeout?: any | number;
  _visibleArea: Rectangle;
  _visiblePages: Rectangle;
  addError: (err: string, ...args: Array<any>) => void;
  askAPIRowNumber?: (key: string) => Promise<number>;
  colFilters?: Array<FilterOption>;
  colOrder?: Record<string, number>;
  colSize?: Record<string, number>;
  colSort?: Array<SortOption>;
  data?: Array<any>;
  errors: Array<string>;
  focused?: boolean;

  get: () => GridlerState;
  getCellContent: (cell: Item) => GridCell;
  getCellsForSelection: (
    selection: Rectangle,
    abortSignal: AbortSignal
  ) => CellArray | GetCellsThunk;
  getGridSelectedRows: () => Array<any>;
  getRowBuffer: (row: number) => Record<string, any>;
  getRowIndexByKey: (key: number | string) => Promise<number | undefined>;
  getState: <K extends keyof GridlerStoreState>(key: K) => GridlerStoreState[K];
  hasLocalData: boolean;
  isEmpty: boolean;

  isValuesInPages: () => boolean
  loadingData?: boolean;
  loadPage: (page: number, clearMode?: 'all' | 'page') => Promise<void>;
  mounted: boolean;
  onCellActivated: (cell: Item) => void;
  onCellClicked: (cell: Item, event: CellClickedEventArgs) => void;
  onCellEdited: (cell: Item, newVal: EditableGridCell) => void;
  onColumnMoved: (from: number, to: number) => void;
  onColumnProposeMove: (startIndex: number, endIndex: number) => boolean;
  onColumnResize: (
    column: GridColumn,
    newSize: number,
    colIndex: number,
    newSizeWithGrow: number
  ) => void;
  onContextClick: (area: string, event: CellClickedEventArgs, col?: number, row?: number) => void;
  onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => void;
  onHeaderMenuClick: (col: number, screenPosition: Rectangle) => void;
  onItemHovered: (args: GridMouseEventArgs) => void;

  onVisibleRegionChanged: (
    r: Rectangle,
    tx: number,
    ty: number,
    extras: {
      freezeRegion?: Rectangle;
      freezeRegions?: readonly Rectangle[];
      selected?: Item;
    }

  ) => void;

  pageSize: number;
  ready: boolean;
  refreshCells: (fromRow?: number, toRow?: number, col?: number) => void;

  reload?: () => Promise<void>;
  renderColumns?: GridlerColumns;
  setState: <K extends keyof GridlerStoreState>(
    key: K,
    value: GridlerStoreState[K]
  ) => void;
  setStateFN: <K extends keyof GridlerStoreState>(
    key: K,
    value: (current: GridlerStoreState[K]) => Partial<GridlerStoreState[K]>
  ) => Promise<void>;
  toCell: <TRowType extends Record<string, string>>(row: TRowType, col: number) => GridCell;
  useAPIQuery?: (index: number) => Promise<Array<Record<string, any>>>;
}

export type GridlerStoreState = GridlerProps & GridlerState;

export type SortOption = { direction: 'asc' | 'desc'; id: string; order?: number };

const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreState, GridlerProps>(
  (set, get) => ({
    _events: new EventTarget(),
    _loadingList: CompactSelection.empty(),
    _page_data: {},
    _refresh: async () => {
      const s = get();
      await s.loadPage(0, 'all');
      await s.refreshCells();
      await s.reload?.();
    },
    _visibleArea: { height: 10000, width: 1000, x: 0, y: 0 },
    _visiblePages: { height: 0, width: 0, x: 0, y: 0 },
    addError: (err: string, ...args: Array<unknown>) => {
      const s = get();
      console.log('Gridler Error', s.uniqueid, err, args);
      set(
        produce((state: GridlerStoreState) => {
          state.errors = [...state.errors, err];
        })
      );
    },
    errors: [],
    get: () => get(),
    getCellContent: (cell: Item) => {
      const state = get();
      const [col, row] = cell;

      const buffer = state.getRowBuffer(row) as Record<string, string> | undefined;

      if (buffer !== undefined) {
        return state.toCell(buffer, col);
      }
      return {
        allowOverlay: false,
        kind: GridCellKind.Loading,
      };
    },
    getCellsForSelection: (selection: Rectangle, _abortSignal: AbortSignal) => {
      return async () => {
        const state = get();
        //const firstPage = Math.max(0, Math.floor(selection.y / state.pageSize));
        //const lastPage = Math.floor((selection.y + selection.height) / state.pageSize);

        await state.setStateFN('_visibleArea', (_cv) => {
          //if (r.x === cv.x && r.y === cv.y && r.width === cv.width && r.height === cv.height)
          //  return cv;
          return selection;
        });

        const result: GridCell[][] = [];

        for (let y = selection.y; y < selection.y + selection.height; y++) {
          const row: GridCell[] = [];
          for (let x = selection.x; x < selection.x + selection.width; x++) {
            row.push(state.getCellContent([x, y]));
          }
          result.push(row);
        }

        //console.log('Gridler:Debug:getCellsForSelection', selection, result);
        return result as CellArray;
      };
    },
    getGridSelectedRows: () => {
      const state = get();
      const buffers: Array<any> = [];
      const page_data = state._page_data;
      const pageSize = state.pageSize;

      if (state._gridSelectionRows) {
        for (const range of state._gridSelectionRows) {
          let buffer = undefined;

          for (const p in page_data) {
            for (const r in page_data[p]) {
              const idx = Number(p) * pageSize + Number(r);
              if (isNaN(idx)) {
                continue;
              }

              if (Number(page_data[p][r]?._rownumber) === range + 1) {
                buffer = page_data[p][r];
                //console.log('Found row', range, idx, page_data[p][r]?._rownumber);
                break;
              } else if (idx === range + 1) {
                buffer = page_data[p][r];
                //console.log('Found row 2', range, idx, page_data[p][r]?._rownumber);
                break;
              }
            }
          }
          if (buffer !== undefined) {
            buffers.push(buffer);
          }
        }
      }
      return buffers;
    },

    getRowBuffer: (row: number) => {
      const state = get();
      //Handle local data
      if (state.data && state.data.length > 0) {
        if (state.data[row] === undefined) {
          return {
            allowOverlay: false,
            kind: GridCellKind.Loading,
          };
        }
        return state.data[row];
      }

      //Handle remote paged data
      const firstPage = Math.max(0, Math.floor(row / state.pageSize));
      const upperPage = state.pageSize * firstPage;
      const index = row - upperPage;

      const rowData = state._page_data?.[firstPage]?.[index];

      return rowData;
    },
    getRowIndexByKey: async (key: number | string) => {
      const state = get();

      let rowIndex = -1;
      if (state.ready) {
        const page_data = state._page_data;
        const pageSize = state.pageSize;
        const keyField = state.keyField ?? 'id';
        for (const p in page_data) {
          for (const r in page_data[p]) {
            const idx = Number(p) * pageSize + Number(r);
            //console.log('Found row', idx, page_data[p][r]?.[keyField], scrollToRowKey);
            if (String(page_data[p][r]?.[keyField]) === String(key)) {
              rowIndex =
                page_data[p][r]?._rownumber > 0 ? page_data[p][r]?._rownumber : idx > 0 ? idx : -1;
              break;
            }
          }
          if (rowIndex > 0) {
            console.log('Local row index', rowIndex, key);
            return rowIndex;
          }
        }

        if (rowIndex > 0) {
          return rowIndex;
        } else if (typeof state.askAPIRowNumber === 'function') {
          const rn = await state.askAPIRowNumber(String(key));
          if (rn && rn >= 0) {
            console.log('Remote row index', rowIndex, key);
            return rn;
          }
        }
      }

      return undefined;
    },
    getState: (key) => {
      return get()[key];
    },
    hasLocalData: false,
    isEmpty: true,
    isValuesInPages: () => {
      const state = get();
      if (state.values && Object.keys(state._page_data).length > 0) {
        let found = false;
        for (const page in state._page_data) {
          const pageData = state._page_data[Number(page)];
          for (const row of pageData) {
            const keyField = state.keyField ?? 'id';
            const rowKey = row?.[keyField];
            if (rowKey !== undefined) {
              const match = state.values.find((v) => String(v?.[keyField]) === String(rowKey));
              if (match) {
                found = true;
                break;
              }
            }
          }
          if (found) {
            return true;
          }
        }
      }
      
      return false
    },
    keyField: 'id',
    loadPage: async (pPage: number, clearMode?: 'all' | 'page') => {
      const state = get();
      const page = pPage < 0 ? 0 : pPage;

      const result = state._events.dispatchEvent(
        new CustomEvent('before_loadPage', {
          detail: { clearMode, page: pPage, state },
        })
      );
      if (!result) {
        return;
      }

      const damageList: { cell: [number, number] }[] = [];
      const colLen = Object.keys(state.renderColumns ?? [1, 2, 3]).length;
      const upperPage = state.pageSize * page;
      const hasPage = state._page_data?.[page]?.length > 0;
      //console.log('loadPage', page, clearMode, { upperPage, hasPage, pz: state.pageSize, colLen });
      if (clearMode === 'all') {
        state._active_requests?.forEach((r) => {
          r.controller?.abort?.();
        });

        state.setState('_page_data', {});
        state.setState('_active_requests', []);

        state.loadPage(page);
        return;
      }

      if (!state.useAPIQuery) {
        console.warn('No useAPIQuery function defined, cannot load page', page);
        return;
      }
      if (!hasPage && clearMode !== 'page') {
        state
          .useAPIQuery?.(page)
          .then((data) => {
            state.setStateFN('_page_data', (cv) => {
              return { ...cv, [page]: data };
            });
            for (let row = page; row <= upperPage + state.pageSize; row++) {
              for (let col = 0; col <= colLen; col++) {
                damageList.push({
                  cell: [col, row],
                });
              }
            }

            state._glideref?.updateCells(damageList);
            state._events.dispatchEvent(
              new CustomEvent('loadPage', {
                detail: { clearMode, data, page: pPage, state },
              })
            );
          })
          .catch((e) => {
            console.error('loadPage Error: ', page, e);
            state._events.dispatchEvent(
              new CustomEvent('loadPage_error', {
                detail: { clearMode, error: e, page: pPage, state },
              })
            );
          });
      }
    },
    maxConcurrency: 1,
    mounted: false,
    onCellActivated: (cell: Item): void => {
      const state = get();
      const [col, row] = cell;

      state._events.dispatchEvent(
        new CustomEvent('onCellActivated', {
          detail: { cell, col, row, state },
        })
      );
      state.glideProps?.onCellActivated?.(cell);
    },
    onCellClicked: (cell: Item, event: CellClickedEventArgs) => {
      const state = get();
      const [col, row] = cell;
      if (state.glideProps?.onCellClicked) {
        state.glideProps?.onCellClicked?.(cell, event);
      }
      if (state.values?.length) {
        if (state.onChange) {
          state.onChange(state.values);
        }
      }

      state._events.dispatchEvent(
        new CustomEvent('onCellClicked', {
          detail: { cell, col, row, state },
        })
      );
    },
    onCellEdited: (cell: Item, newVal: EditableGridCell) => {
      const state = get();
      const [, row] = cell;
      //const current = state._editData?.[row];
      //if (current === undefined) return;
      //todo: complete
      state._events.dispatchEvent(
        new CustomEvent('onCellEdited', {
          detail: { cell, newVal, row, state },
        })
      );
      state.glideProps?.onCellEdited?.(cell, newVal);
    },
    onColumnMoved: (from: number, to: number) => {
      const s = get();
      const fromItem = s.renderColumns?.[from];
      const toItem = s.renderColumns?.[to];
      if (fromItem?.disableMove || toItem?.disableMove) {
        return;
      }

      s.setStateFN('colOrder', (cols) => {
        const renderCols =
          cols ??
          s.renderColumns
            ?.map((col, i) => [col.id, i])
            .reduce((acc, [id, i]) => ({ ...acc, [id]: i }), {});

        if (!fromItem?.id || !toItem?.id) {
          return renderCols;
        }

        return { ...renderCols, [fromItem?.id]: to, [toItem?.id]: from };
      });
    },

    onColumnProposeMove: (startIndex: number, endIndex: number) => {
      const s = get();
      const fromItem = s.renderColumns?.[startIndex];
      const toItem = s.renderColumns?.[endIndex];
      if (fromItem?.disableMove || toItem?.disableMove) {
        return false;
      }
      return true;
    },
    onColumnResize: (
      column: GridColumn,
      newSize: number,
      _colIndex: number,
      _newSizeWithGrow: number
    ) => {
      const s = get();
      const col = s.renderColumns?.find((col) => col.id === column.id);
      if (col?.disableResize) {
        return;
      }
      if (col?.maxWidth && newSize < col?.maxWidth) {
        return;
      }

      if (col) {
        s.setStateFN('colSize', (cols) => {
          if (cols && col && cols[col.id] === newSize) {
            return cols;
          }
          return { ...cols, [col.id]: newSize };
        });
      }
    },
    onContextClick: (area: string, event: CellClickedEventArgs, col?: number, row?: number) => {
      const s = get();
      const coldef = s.renderColumns?.[col ?? -1];

      const items =
        area === 'menu'
          ? [{ leftSection: <IconGrid4x4 size={16} />, title: s.title ?? 'Grid' }]
          : coldef
            ? [
                { leftSection: <IconGrid4x4 size={16} />, title: s.title ?? 'Grid' },
                {
                  items: [
                    {
                      label: 'Sort Ascending',
                      leftSection: <SpriteImage sprite={SortUpSprite} />,
                      onClick: () => {
                        s.setStateFN('colSort', (c) => {
                          const cols = [...(c ?? [])];
                          const idx = cols.findIndex((search) => search.id === coldef.id);
                          const dir = 'asc';
                          if (idx < 0) {
                            const newSort: SortOption = {
                              direction: dir,
                              id: coldef.id,
                              order: cols?.length,
                            };
                            cols.push(newSort);
                          } else if (idx >= 0) {
                            cols[idx].direction = dir;
                          }

                          return cols;
                        });
                      },
                    },
                    {
                      label: 'Sort Descending',
                      leftSection: <SpriteImage sprite={SortDownSprite} />,
                      onClick: () => {
                        s.setStateFN('colSort', (c) => {
                          const cols = [...(c ?? [])];
                          const idx = cols.findIndex((search) => search.id === coldef.id);
                          const dir = 'desc';
                          if (idx < 0) {
                            const newSort: SortOption = {
                              direction: dir,
                              id: coldef.id,
                              order: cols?.length,
                            };
                            cols.push(newSort);
                          } else if (idx >= 0) {
                            cols[idx].direction = dir;
                          }

                          return cols;
                        });
                      },
                    },
                    {
                      label: `Filter ${coldef?.title ?? coldef?.id}`,
                    },
                    {
                      renderer: <ColumnFilterSet column={coldef} storeState={get()} />,
                    },
                  ],
                  label: `Column Settings for ${coldef?.title ?? coldef?.id}`,
                  leftSection: <IconGrid4x4 size={16} />,
                },
              ]
            : [];
      s.hideMenu?.(area);

      s.showMenu?.(area, {
        items:
          coldef?.getMenuItems?.(
            area,
            s,
            col && row ? s.getRowBuffer(row) : undefined,
            coldef,
            items
          ) ??
          s.getMenuItems?.(area, s, col && row ? s.getRowBuffer(row) : undefined, coldef, items) ??
          items,
        //@ts-expect-error Check bounds
        x: event.clientX ?? event.bounds?.x,
        //@ts-expect-error Check bounds
        y: event.clientY ?? event.bounds?.y,
      });
    },
    onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => {
      const s = get();
      event.preventDefault();
      const col = s.renderColumns?.[colIndex];
      if (!col) {
        return;
      }
      if (!col.disableSort) {
        s.setStateFN('colSort', (c) => {
          const cols = [...(c ?? [])];
          const idx = cols.findIndex((search) => search.id === col.id);

          if (idx < 0) {
            const newSort: SortOption = { direction: 'asc', id: col.id, order: cols?.length };
            cols.push(newSort);
          } else if (idx >= 0 && cols[idx].direction === 'asc') {
            cols[idx].direction = 'desc';
          } else if (idx >= 0 && cols[idx].direction === 'desc') {
            cols.splice(idx, 1);
          }

          return cols;
        });
      }
    },
    onHeaderMenuClick: (col: number, screenPosition: Rectangle) => {
      const s = get();
      const coldef = s.renderColumns?.[col];
      if (!coldef) {
        return;
      }

      const sortItems = [
        {
          label: `Sort ${coldef?.title ?? coldef?.id}`,
        },
        {
          label: 'Sort Ascending',
          leftSection: <SpriteImage sprite={SortUpSprite} />,
          onClick: () => {
            s.setStateFN('colSort', (c) => {
              const cols = [...(c ?? [])];
              const idx = cols.findIndex((search) => search.id === coldef.id);
              const dir = 'asc';
              if (idx < 0) {
                const newSort: SortOption = { direction: dir, id: coldef.id, order: cols?.length };
                cols.push(newSort);
              } else if (idx >= 0) {
                cols[idx].direction = dir;
              }

              return cols;
            });
          },
        },
        {
          label: 'Sort Descending',
          leftSection: <SpriteImage sprite={SortDownSprite} />,
          onClick: () => {
            s.setStateFN('colSort', (c) => {
              const cols = [...(c ?? [])];
              const idx = cols.findIndex((search) => search.id === coldef.id);
              const dir = 'desc';
              if (idx < 0) {
                const newSort: SortOption = { direction: dir, id: coldef.id, order: cols?.length };
                cols.push(newSort);
              } else if (idx >= 0) {
                cols[idx].direction = dir;
              }

              return cols;
            });
          },
        },
        {
          isDivider: true,
        },
      ];
      const items = [
        ...(coldef.disableSort ? [] : sortItems),
        {
          label: `Filter ${coldef?.title ?? coldef?.id}`,
        },
        {
          renderer: <ColumnFilterSet column={coldef} storeState={get()} />,
        },
        {
          isDivider: true,
        },
        {
          id: 'refesh',
          label: `Refresh`,
          onClick: () => {
            const s = get();
            s._refresh?.();
          },
        },
      ];
      s.hideMenu?.('header-menu');
      s.showMenu?.('header-menu', {
        items:
          coldef?.getMenuItems?.('header-menu', s, undefined, coldef, items) ??
          s.getMenuItems?.('header-menu', s, undefined, coldef, items),
        x: screenPosition.x,
        y: screenPosition.y,
      });
    },
    onItemHovered: (args: GridMouseEventArgs) => {
      const s = get();
      s.setState('_activeTooltip', undefined);
      if (args.kind === 'cell') {
        //_activeTooltip
        const coldef = s.renderColumns?.[args.location[0]];
        if (coldef?.tooltip && typeof coldef?.tooltip === 'string') {
          s.setState('_activeTooltip', coldef?.tooltip);
        } else if (coldef?.tooltip && typeof coldef?.tooltip === 'function') {
          const buffer = s.getRowBuffer(args.location[1]);
          s.setState('_activeTooltip', coldef?.tooltip(buffer, args.location[1], args.location[0]));
        }
      }
    },
    onVisibleRegionChanged: (
      region: Rectangle,
      _tx: number,
      _ty: number,
      _extras: {
        freezeRegion?: Rectangle;
        freezeRegions?: readonly Rectangle[];
        selected?: Item;
      }
    ) => {
      const state = get();
      if (state._scrollTimeout) {
        clearTimeout(state._scrollTimeout);
      }

      const onVisibleRegionChangedDebounced = () => {
        //console.log('Gridler:Debug:VisibleRegionChanged', r);
        const state = get();
        const firstPage = Math.max(0, Math.floor(region.y / state.pageSize));
        // const lastPage = Math.floor((region.y + region.height) / state.pageSize);
        // const upperPage = state.pageSize * firstPage;

        const previousPage = Math.max(0, Math.floor(state._visiblePages.y / state.pageSize));
        const pageDif = firstPage - previousPage;
        // console.log(
        //   'Page dif',
        //   pageDif,
        //   { previousPage, firstPage, lastPage, onder: pageDif - 1 === lastPage },
        //   region
        // );
        // if (pageDif - 1 === lastPage) {
        //   console.log('Is onder', lastPage, pageDif);
        // }

        if (state.progressiveScroll && pageDif > 1) {
          const state = get();
          const newpos = (previousPage + 2) * state.pageSize * (state.rowHeight ?? 22);
          // console.log('Pending scroll exec', newpos, {
          //   pageSize: state.pageSize,
          //   rowHeight: state.rowHeight
          // });
          state._glideref?.scrollTo(
            0,
            {
              amount: newpos,
              unit: 'px',
            },
            'vertical'
          );

          return;
        }
        //console.log('VIsi change', { firstPage, lastPage }, state?._page_data[lastPage]);
        // if (state._active_requests && state._active_requests.filter((f)=>f.page > firstPage - 1)) {

        // }

        //console.log('Gridler:Debug:VisibleRegionChanged', region, firstPage, lastPage);
        state.setState('_visiblePages', region);
      };

      state.setState(
        '_scrollTimeout',
        setTimeout(() => {
          onVisibleRegionChangedDebounced();
        }, 100)
      );
    },
    pageSize: 50,
    ready: false,
    refreshCells: (fromRow?: number, toRow?: number, col?: number) => {
      const state = get();
      const damageList: { cell: [number, number] }[] = [];
      const colLen = Object.keys(state.renderColumns ?? [1, 2, 3]).length;

      const from = fromRow && fromRow > 0 ? fromRow : 0;
      const to = toRow && toRow >= from ? toRow : from + state.pageSize;

      for (let row = from; row <= to; row++) {
        if (col && col > 0) {
          damageList.push({
            cell: [col, row],
          });
        } else {
          for (let c = 0; c <= colLen; c++) {
            damageList.push({
              cell: [c, row],
            });
          }
        }
      }

      state._glideref?.updateCells(damageList);
    },
    setState: (key, value) => {
      set(
        produce((state) => {
          state[key] = value;
        })
      );
    },
    setStateFN: (key, value) => {
      const p = new Promise<void>((resolve, reject) => {
        set(
          produce((state) => {
            if (typeof value === 'function') {
              state[key] = (value as (value: unknown) => unknown)(state[key]);
            } else {
              reject(new Error(`Not a function ${value}`));
              throw Error(`Not a function ${value}`);
            }
          })
        );
        resolve();
      });

      return p;
    },
    toCell: <T extends Record<string, string>>(row: T, col: number): GridCell => {
      const s = get();
      const columns = s.renderColumns;
      const coldef: GridlerColumn | undefined = columns?.[col];
      const ref: string = coldef?.id ?? coldef?.title ?? String(col);

      if (ref === undefined || ref === null || row === undefined || row === null) {
        if (coldef?.Cell) {
          return coldef?.Cell(row, col, ref, undefined, s) as GridCell;
        }
        if (s.RenderCell) {
          return s.RenderCell(row, col, ref, undefined, s);
        }

        return {
          allowOverlay: false,
          kind: GridCellKind.Loading,
        };
      }

      try {
        const val = String(ref).includes('.') ? (getNestedValue(ref, row) ?? '') : row?.[ref];

        if (coldef?.Cell) {
          return { kind: GridCellKind.Text, ...coldef?.Cell(row, col, ref, val, s) } as GridCell;
        }
        if (s.RenderCell) {
          return s.RenderCell(row, col, ref, val, s);
        }

        return {
          allowOverlay: true,
          data: val ?? '',
          displayData: String(val ?? ''),
          kind: GridCellKind.Text,
        };
      } catch (e) {
        if (s.RenderCell) {
          return s.RenderCell(row, col, ref, row?.[ref ?? ''], s);
        }

        return {
          allowOverlay: false,
          kind: GridCellKind.Loading,
          skeletonWidthVariability: 50,
        };
      }
    },
    total_rows: 1000,
    uniqueid: getUUID()
  }),
  (props) => {
    const [setState, getState] = props.useStore((s) => [s.setState, s.getState]);

    const menus = useMantineBetterMenus();

    useEffect(() => {
      const onMounted = getState('onMounted');
      if (typeof onMounted === 'function') {
        onMounted(getState, setState);
      }
      setState('mounted', true);
      if (window && window.document) {
        const portalElement = window.document.getElementById('portal');
        if (!portalElement) {
          const div = window.document.createElement('div');
          div.id = 'portal';
          div.setAttribute('data-gridler-portal', props.uniqueid);
          window.document.body.appendChild(div);
        }
      }
      getState('_events').dispatchEvent(
        new CustomEvent('mounted', {
          detail: {},
        })
      );
      return () => {
        const onUnMounted = getState('onUnMounted');
        setState('mounted', false);
        getState('_events').dispatchEvent(
          new CustomEvent('unmounted', {
            detail: {},
          })
        );
        if (typeof onUnMounted === 'function') {
          onUnMounted();
        }
      };
    }, [setState, getState]);

    getState('_events').addEventListener('reload', (_e: Event) => {
      getState('_refresh')?.();
    });

    return {
      ...props,

      colSort: props.defaultSort ?? getState('colSort') ?? [],
      hideMenu: props.hideMenu ?? menus.hide,
      scrollToRowKey: props.scrollToRowKey ?? props.selectedRowKey ?? getState('scrollToRowKey'),
      showMenu: props.showMenu ?? menus.show,
      total_rows: getState('total_rows') ?? props.total_rows,
    };
  }
);

export type useStoreReturnType = ReturnType<typeof useGridlerStore>;

export { Provider, useGridlerStore };
