import { CompactSelection } from '@glideapps/glide-data-grid';
import { useDebouncedCallback } from '@mantine/hooks';
import React, { useEffect, useRef } from 'react';

import { useGridlerStore } from './GridlerStore';

//The computer component does not need to be recalculated on every render, so we use React.memo to prevent unnecessary re-renders.
export const Computer = React.memo(() => {
  const refFirstRun = useRef(0);
  const refLastSearch = useRef('');
  const refLastFilters = useRef<unknown>(null);
  const {
    _glideref,
    _gridSelectionRows,
    colFilters,
    colOrder,
    colSize,
    colSort,
    columns,
    getRowIndexByKey,
    getState,
    loadPage,
    ready,

    scrollToRowKey,
    searchStr,
    selectedRowKey,
    selectFirstRowOnMount,
    setState,
    setStateFN,
    values,
  } = useGridlerStore((s) => ({
    _glideref: s._glideref,
    _gridSelectionRows: s._gridSelectionRows,
    colFilters: s.colFilters,
    colOrder: s.colOrder,
    colSize: s.colSize,
    colSort: s.colSort,
    columns: s.columns,
    getRowIndexByKey: s.getRowIndexByKey,
    getState: s.getState,
    loadPage: s.loadPage,
    ready: s.ready,

    scrollToRowKey: s.scrollToRowKey,
    searchStr: s.searchStr,
    selectedRowKey: s.selectedRowKey,
    selectFirstRowOnMount: s.selectFirstRowOnMount,
    setState: s.setState,
    setStateFN: s.setStateFN,
    uniqueid: s.uniqueid,
    values: s.values,
  }));

  const debouncedDoSearch = useDebouncedCallback(
    (searchStr: string) => {
      loadPage(0, 'all').then(() => {
        getState('refreshCells')?.();
        getState('_events')?.dispatchEvent?.(
          new CustomEvent('onSearched', {
            detail: { search: searchStr },
          })
        );
      });
    },
    {
      delay: 300,
      leading: false,
    }
  );

  //When values change, update selection
  useEffect(() => {
    const searchSelection = async (values: Array<Record<string, unknown>>) => {
      const page_data = getState('_page_data');
      const pageSize = getState('pageSize');
      const keyField = getState('keyField') ?? 'id';
      const rowIndexes = [];
      for (const vi in values as Array<Record<string, unknown>>) {
        let rowIndex = -1;
        const key = String(
          typeof values?.[vi] === 'object'
            ? values?.[vi]?.[keyField]
            : typeof values?.[vi] === 'string'
              ? values?.[vi]
              : undefined
        );
        if (!key) {
          continue;
        }
        for (const p in page_data) {
          for (const r in page_data[p]) {
            const idx = Number(p) * pageSize + Number(r);

            if (String(page_data[p][r]?.[keyField]) === key) {
              //console.log('Found row S', idx, page_data[p][r], page_data[p][r]?.[keyField], key);
              rowIndex = idx;
              break;
            }
          }
          if (rowIndex >= 0) {
            rowIndexes.push(rowIndex);
            break;
          }
        }

        if (rowIndex >= 0) {
          rowIndexes.push(rowIndex);
        } else {
          const idx = await getRowIndexByKey(key);
          if (idx !== null) {
            rowIndexes.push(idx);
          }
        }
      }

      return rowIndexes;
    };

    if (values) {
      searchSelection(values).then((rowIndexes) => {
        let rows = CompactSelection.empty();
        rowIndexes.forEach((r) => {
          if (r !== undefined) {
            rows = rows.add(r);
          }
        });

        setStateFN('_gridSelectionRows', () => {
          return rows;
        });

        setStateFN('_gridSelection', (c) => ({
          columns: c?.columns ?? CompactSelection.empty(),
          ...c,
          rows,
        }));
      });
    }
  }, [values]);

  //Fire onChange when selection changes
  useEffect(() => {
    const onChange = getState('onChange');
    if (onChange && typeof onChange === 'function') {
      const getGridSelectedRows = getState('getGridSelectedRows');
      const buffers = getGridSelectedRows();

      const _values = getState('values');

      if (JSON.stringify(_values) !== JSON.stringify(buffers)) {
        onChange(buffers);
      }
    }
  }, [_gridSelectionRows, _gridSelectionRows?.length, getState]);

  useEffect(() => {
    setState(
      'renderColumns',
      columns?.map((c) => ({
        ...c,
        hasMenu: c?.hasMenu ?? true,
        icon: 'sort',
      }))
    );
  }, [columns]);

  useEffect(() => {
    if (searchStr === undefined || searchStr === null) {
      refLastSearch.current = '';
      return;
    }
    if (refLastSearch.current !== searchStr) {
      debouncedDoSearch(searchStr);
      refLastSearch.current = searchStr;
    }
  }, [searchStr]);

  useEffect(() => {
    if (!colSort) {
      return;
    }

    setState('_gridSelection', {
      columns: CompactSelection.empty(),
      current: undefined,
      rows: CompactSelection.empty(),
    });

    setState('_gridSelectionRows', CompactSelection.empty());
    setStateFN('renderColumns', (cols) => {
      return cols?.map((c) => ({
        ...c,
        icon:
          c.id && colSort?.find((col) => col.id === c.id)?.direction
            ? colSort?.find((col) => col.id === c.id)?.direction === 'asc'
              ? 'sortup'
              : 'sortdown'
            : (c.defaultIcon ?? 'sort'),
      }));
    }).then(() => {
      loadPage(0, 'all').then(() => {
        getState('refreshCells')?.();
        getState('_events')?.dispatchEvent?.(
          new CustomEvent('onColumnSorted', {
            detail: { cols: colSort },
          })
        );
      });
    });
  }, [colSort]);

  useEffect(() => {
    if (!colFilters) {
      return;
    }

    if (JSON.stringify(refLastFilters.current) !== JSON.stringify(colFilters)) {
      loadPage(0, 'all').then(() => {
        getState('refreshCells')?.();
        getState('_events')?.dispatchEvent?.(
          new CustomEvent('onColumnFiltered', {
            detail: { filters: colFilters },
          })
        );
      });
      refLastFilters.current = colFilters;
    }
  }, [colFilters]);

  useEffect(() => {
    if (!colSize) {
      return;
    }
    setStateFN('renderColumns', (cols) => {
      return cols?.map((c) => ({
        ...c,
        width: c.id && colSize?.[c.id] ? colSize?.[c.id] : c.width,
      }));
    }).then(() => {
      getState('refreshCells')?.();
    });
  }, [colSize]);

  useEffect(() => {
    if (!colOrder) {
      return;
    }

    setStateFN('renderColumns', (cols) => {
      const result = cols?.sort((a, b) => {
        if (colOrder[a.id] > colOrder[b.id]) {
          return 1;
        }
        return -1;
      });

      return result;
    }).then(() => {
      getState('refreshCells')?.();
    });
  }, [colOrder]);

  //Initial Load
  useEffect(() => {
    if (!_glideref) {
      return;
    }
    if (refFirstRun.current > 0) {
      return;
    }
    refFirstRun.current = 1;
    loadPage(0).then(() => {
      getState('refreshCells')?.();
    });
  }, [ready, loadPage]);

  //Logic to select first row on mount
  useEffect(() => {
    const _events = getState('_events');

    const loadPage = () => {
      const selectFirstRowOnMount = getState('selectFirstRowOnMount');
      const ready = getState('ready');

      if (ready && selectFirstRowOnMount) {
        const scrollToRowKey = getState('scrollToRowKey');
        if (scrollToRowKey && scrollToRowKey >= 0) {
          return;
        }

        const keyField = getState('keyField') ?? 'id';
        const page_data = getState('_page_data');

        const firstBuffer = page_data?.[0]?.[0];
        const firstRow = firstBuffer?.[keyField] ?? -1;
        const currentValues = getState('values') ?? [];

        if (!firstBuffer) {
          return;
        }

        if (firstRow && firstRow > 0 && (currentValues.length ?? 0) === 0) {
          const newValues = [firstBuffer];

          const onChange = getState('onChange');
          if (onChange) {
            onChange(newValues);
          } else {
            setState('values', newValues);
          }

          setState('scrollToRowKey', firstRow);
        }
      }
    };

    _events?.addEventListener('loadPage', loadPage);

    return () => {
      _events?.removeEventListener('loadPage', loadPage);
    };
  }, [ready, selectFirstRowOnMount, values]);

  /// logic to apply the selected row.
  // useEffect(() => {
  //   const ready = getState('ready');
  //   const ref = getState('_glideref');
  //   const getRowIndexByKey = getState('getRowIndexByKey');

  //   if (scrollToRowKey && ref && ready) {
  //     getRowIndexByKey?.(scrollToRowKey).then((r) => {
  //       if (r !== undefined) {
  //         //console.log('Scrolling to selected row:', scrollToRowKey, r);
  //         ref.scrollTo(0, r);
  //         getState('_events').dispatchEvent(
  //           new CustomEvent('scrollToRowKeyFound', {
  //             detail: { rowNumber: r, scrollToRowKey: scrollToRowKey },
  //           })
  //         );
  //       }
  //     });
  //   }
  // }, [scrollToRowKey]);

  useEffect(() => {
    const ready = getState('ready');
    const ref = getState('_glideref');
    const getRowIndexByKey = getState('getRowIndexByKey');
    const key = selectedRowKey ?? scrollToRowKey;

    if (key && ref && ready) {
      //console.log('Computer:Scrolling to key:', key);
      getRowIndexByKey?.(key).then((r) => {
        if (r !== undefined) {
          if (selectedRowKey) {
            const onChange = getState('onChange');
            const selected = [{ [getState('keyField') ?? 'id']: selectedRowKey }];
            if (JSON.stringify(getState('values')) !== JSON.stringify(selected)) {
              if (onChange) {
                onChange(selected);
              } else {
                setState('values', selected);
              }
            }
          }

          ref.scrollTo(0, r);
          getState('_events').dispatchEvent(
            new CustomEvent('scrollToRowKeyFound', {
              detail: {
                rowNumber: r,
                scrollToRowKey: scrollToRowKey,
                selectedRowKey: selectedRowKey,
              },
            })
          );
        }
      });
    }
  }, [scrollToRowKey, selectedRowKey]);

  return <></>;
});

Computer.displayName = 'Gridler-Computer';
