import { useState, useCallback, useEffect, useRef } from 'react';
import {
  useReactTable,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  type SortingState,
  type ColumnFiltersState,
  getFacetedRowModel,
  getFacetedUniqueValues,
  type RowSelectionState,
} from '@tanstack/react-table';
import { dateSortingFn, multiSelectFilter } from './helpers';
import { SortOrder } from './constants';
import type { EditableTableColumnDef, EditableTableProps } from './types';
import EditableTableHeader from './editable-table-header';
import FocusHandlerContextProvider from './focus-handler/focus-handler-provider';
import EditableTableBody from './table-body/editable-table-body';
import TableContextProvider from './table-context/table-context-provider';
import { twMerge } from '@trail-ui/shared-utils';
import { EditableTablePagination } from './pagination/pagination';
import EditabletableFooter from './table-footer/editable-table-footer';

export default function TanstackEditableTable<TData>({
  tableId,
  classNames,

  tableName,
  data,
  columns,
  isLoading,
  //Save data callback
  handleSaveData,
  idSelector,

  selectAllCheckBoxName,
  setTable,
  setColumnFilters,
  hiddenColumnIds,
  isCellDisabled,

  //Server pagination props
  showPagination = true,
  serverPagination = false,
  totalCount,
  currentPage = 1,
  onPageChange,
  currentPageSize,
  onPageSizeChange,
  defaultSortingHeaders,
  handleSort,
  globalFilter,
  setGlobalFilter,
  paginationSizeOptions,

  //Table footer props
  showTableFooter = false,

  //row selection
  onSelectionChange,
}: EditableTableProps<TData>) {
  const [tableData, setTableData] = useState(data || []); //When data changes, we update the state to render the table, if not maintained here then user will have to update the data in parent, which would be cumbersome
  const [sorting, setSorting] = useState<SortingState>(defaultSortingHeaders || []);
  const [tableColumnFilters, setTableColumnFilters] = useState<ColumnFiltersState>([]);
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  const tableRef = useRef<HTMLTableElement>(null);

  // ⏳ Pagination state
  const [pagination, setPagination] = useState({
    pageSize: currentPageSize ?? 100,
    pageIndex: currentPage - 1,
  });

  // 🔁 Sync external data updates

  const updateData = useCallback((rowId: string, accessorKey: keyof TData | '', value: unknown) => {
    setTableData((prev) => {
      const updatedData = [...prev];

      const rowIndex = updatedData.findIndex((r) => idSelector(r) === rowId);
      if (rowIndex === -1) {
        return prev;
      }

      updatedData[rowIndex] = {
        ...updatedData[rowIndex],
        [accessorKey]: value,
      };

      return updatedData;
    });
  }, []);

  // 📌 Server pagination uses manual control
  const totalPages =
    serverPagination && totalCount && pagination.pageSize
      ? Math.ceil(totalCount / pagination.pageSize)
      : Math.ceil(tableData.length / pagination.pageSize);

  const table = useReactTable({
    data: tableData,
    columns,
    state: {
      sorting,
      pagination,
      columnFilters: tableColumnFilters,
      globalFilter,
      rowSelection,
      ...(hiddenColumnIds?.length && {
        columnVisibility: Object.fromEntries(hiddenColumnIds.map((col: string) => [col, false])),
      }),
    },
    onRowSelectionChange: (updater) => {
      setRowSelection((prev) => {
        const newSelection = typeof updater === 'function' ? updater(prev) : updater;
        return newSelection;
      });
    },
    getRowId: idSelector,
    manualPagination: serverPagination,
    ...(serverPagination && { pageCount: totalPages }),
    onPaginationChange: setPagination,
    manualSorting: serverPagination,
    getSortedRowModel: serverPagination ? undefined : getSortedRowModel(),
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    onSortingChange: setSorting,
    getFacetedRowModel: getFacetedRowModel(),
    getFacetedUniqueValues: getFacetedUniqueValues(),
    onColumnFiltersChange: setTableColumnFilters,
    onGlobalFilterChange: setGlobalFilter,
    enableSortingRemoval: false,
    sortingFns: { dateSortingFn },
    filterFns: { multiSelectFilter },
    meta: {
      updateData,
    },
  });

  // Sync sort state to server
  useEffect(() => {
    if (serverPagination && sorting.length > 0 && handleSort) {
      const { id, desc } = sorting[0];
      const order = desc === undefined ? SortOrder.DEFAULT : desc ? SortOrder.DESC : SortOrder.ASC;
      handleSort({ columnId: id, order });
    }
  }, [sorting]);

  useEffect(() => {
    if (onSelectionChange) {
      const ids = Object.keys(rowSelection).filter((id) => rowSelection[id]);

      onSelectionChange(ids);
    }
  }, [rowSelection]);

  // Expose filters externally
  if (setColumnFilters) {
    setColumnFilters(tableColumnFilters);
  }

  // 🧮 Local vs server totals
  const totalResults = serverPagination
    ? (totalCount ?? 0)
    : table.getFilteredRowModel().rows.length;

  useEffect(() => {
    if (JSON.stringify(data) !== JSON.stringify(tableData)) {
      setTableData(data);
    }
  }, [data]);

  useEffect(() => setTable?.(table), [table]);

  // ONLY sync pagination if props were actually provided
  useEffect(() => {
    if (serverPagination && currentPage !== undefined) {
      setPagination((prev) => ({ ...prev, pageIndex: currentPage - 1 }));
    }
  }, [serverPagination, currentPage]);

  useEffect(() => {
    if (serverPagination && currentPageSize !== undefined) {
      setPagination((prev) => ({ ...prev, pageSize: currentPageSize }));
    }
  }, [serverPagination, currentPageSize]);

  return (
    <TableContextProvider<TData>
      handleSaveData={handleSaveData}
      idSelector={idSelector}
      table={table}
      tableName={tableName}
      groupData={[]}
      allowBulkGroupSelection={false}
      tableRef={tableRef}
      doesRowHeaderExist={table
        .getAllColumns()
        .map((col) => col.columnDef as EditableTableColumnDef<TData>)
        .some((col) => col?.columnType === 'th')}
      hasFooter={showTableFooter}
      serverPagination={serverPagination}
    >
      <FocusHandlerContextProvider table={table} groupData={[]}>
        <div
          id={tableId}
          className={twMerge('flex max-h-full max-w-full flex-col gap-4', classNames?.wrapper)}
        >
          <div
            className={twMerge(
              'relative h-full max-h-[100%-3.5rem] overflow-auto rounded border border-neutral-200',
              classNames?.tableWrapper,
            )}
          >
            <table
              ref={tableRef}
              // biome-ignore lint/a11y/useSemanticElements: <explanation>
              role="grid"
              aria-label={tableName ?? ''}
              className={twMerge('w-full', classNames?.table)}
            >
              <EditableTableHeader
                table={table}
                tableName={tableName}
                selectAllCheckBoxName={selectAllCheckBoxName}
              />
              <EditableTableBody
                table={table}
                columns={columns}
                isLoading={isLoading}
                isCellDisabled={isCellDisabled}
              />
              {showTableFooter && <EditabletableFooter />}
            </table>
          </div>

          {showPagination && (
            <EditableTablePagination
              totalResults={totalResults}
              pagination={pagination}
              table={table}
              onPageChange={onPageChange}
              onPageSizeChange={onPageSizeChange}
              paginationSizeOptions={paginationSizeOptions}
            />
          )}
        </div>
      </FocusHandlerContextProvider>
    </TableContextProvider>
  );
}
