import { Table, TableColumn as WSRTableColumn } from '@wix/design-system';
import React, { ReactElement } from 'react';
import { observer } from 'mobx-react-lite';
import { FiltersMap, KeyedItem } from '@wix/bex-core';
import { CollectionCard } from '../CollectionCard';
import { TableColumn } from '../../model';
import { TableState } from '../../state';
import { useTableCollectionSyncProps } from './useTableCollectionSyncProps';
import type { TableDragAndDropType } from '../TableDragAndDrop';
import {
  CollectionTableWSRTable,
  CollectionTableWSRTableBaseProps,
} from './CollectionTableWSRTable';
import { CollectionTableContent } from './CollectionTableContent';
import { PageInfiniteScrollLoader } from '../PageInfiniteScrollLoader';
import { useActionCellColumn } from '../ActionCell';
import {
  BulkActionModalRenderProp,
  BulkActionToolbarRenderProp,
  SingleCollectionBulkActionModal,
  SingleCollectionBulkActionToolbar,
} from '../BulkAction';
import { CollectionTableActionCellProps } from './CollectionTableBaseCommonProps';
import {
  type DataExtensionElement,
  type DataExtensionType,
} from '../DataExtension';
import type { FieldsSourceProvider } from '../fieldsSourceProvider';
import { useCreateWSRColumnBase } from '../../hooks/useCreateWSRColumnBase';
import type { TagsElement } from '../Tags';
import { TablePlaceholderStates } from '../TablePlaceholderStates/TablePlaceholderStates';
import { useScrollableContentContext } from '../../providers';
import { st, classes } from './CollectionTable.st.css.js';
import { useIsMobile } from '../../hooks/useIsMobile';
import { useCollectionTableResize } from '../../hooks/useCollectionTableResize';
import { SearchableCollectionContent } from '../SearchableCollectionContent/SearchableCollectionContent';

export interface CollectionTableBaseProps<T, F extends FiltersMap>
  extends Omit<CollectionTableWSRTableBaseProps<T, T, F>, 'resizable'>,
    CollectionTableActionCellProps<T, F> {
  /**
   * Defines the columns of the table. Accepts an array of [`TableColumn`](./?path=/story/common-types--tablecolumn) objects.
   * @overrideType [TableColumn](./?path=/story/common-types--tablecolumn)[]
   * @external
   */
  columns: TableColumn<T>[];

  /**
   * Whether to show a checkbox column for selecting items.
   * @default false
   * @external
   */
  showSelection?: boolean;
  /**
   * A callback method to be called on row click. Signature: `onRowClick(rowData, rowNum)`. To enable hover effect you should set this prop.
   * @external
   */
  onRowClick?: (item: T, index: number) => void;
  /**
   * A [`<ExportButton />`](./?path=/story/features-export--exportto) react element to be added to the toolbar.
   */
  exportButton?: ReactElement;

  /**
   * Adds functionality to allow visitors to reorder items manually.
   * For more information, see the [Drag and Drop Overview](./?path=/story/features-sort-drag-and-drop--overview).
   * @overrideType [TableDragAndDrop](./?path=/story/features-sort-drag-and-drop--tabledraganddrop)
   * @external
   */
  dragAndDrop?: TableDragAndDropType;

  /**
   * Data extension implementation: `import { DataExtension } from '@wix/patterns';`
   */
  dataExtension?: DataExtensionType | DataExtensionElement;

  /**
   * Attaches a field source to the table — the way to add a source's fields,
   * filters, and field management. Build it with `tableSchemaSource(...)` for a
   * schema source (also spread its `collectionConfig` into the collection hook),
   * or `dataExtensionTableSource(...)` for DataExtension, which extends the
   * consumer's own collection.
   */
  /** A fields source (e.g. `dataExtensionTableSource(...)`) whose fields become
   *  columns and whose field management wires into the table. Schema sources
   *  attach via the `@wix/patterns/schema` collection hooks instead. */
  fieldsSource?: FieldsSourceProvider;

  tags?: TagsElement<T, F>;

  forceResize?: boolean;

  /**
   * A function that receives the item of the row and returns a string for tooltip content.
   * @external
   */
  checkboxTooltipContent?: (rowData: KeyedItem<T>) => string | undefined;

  /**
   * Render a custom toolbar when more than 1 item is selected.<br>
   * The function accepts the following parameters
   *   * `selectedValues`: The items that were checked via the selection column
   *   * `uncheckedValues`: The items that were unchecked via the selection column
   *   * `allSelected`: Indicates whether the "Select All" checkbox is checked
   *   * `openModal`: Opens the modal that contains the component from `bulkActionModal` render prop
   *   * `clearSelection`: Clears all existing selection, call this after the action on the selected items was done
   *   * `total`: Total items in the server (taken from [`fetchData`](./?path=/story/common-hooks--usecollection) return value).
   *   * `openConfirmModal`: Opens a `<MessageModalLayout/>` to confirm an action about to be applied on the selected items.
   *   * `query`: ComputedQuery<F> - Representation of the current paging, sorting & filtering applied. Usually for using together with bulk action such as `updateAll` and `deleteAll`
   *
   * > When passed, enables `showSelection` by default.
   * @external
   */
  bulkActionToolbar?: BulkActionToolbarRenderProp<T, F>;
  /**
   * When true, wraps every column header with the AddField hover popover.
   * The "+" button is revealed when the consumer hovers a column header and
   * triggers either the schema's `addField` or the data-extension's custom
   * fields modal. Off by default.
   * @external
   */
  showAddFieldButton?: boolean;

  /**
   * Opens a modal from the [`MultiBulkActionToolbar`](./?path=/story/features-actions-bulk-actions--multibulkactiontoolbar).
   * Supported parameters:
   *  + `selectedValues`: Selected item values.
   *  + `uncheckedValues`: Unselected item values.
   *  + `isSelectAll`: [bool] Whether the **Select All** option is selected.
   *  + `closeModal`: Closes the modal.
   *  + `query`: [object] Instance of [ComputedQuery](./?path=/story/common-types--computedquery) that represents the query that resulted in an empty state. Representation of the current paging, sorting and filtering applied. Usually for using together with bulk action such as `updateAll` and `deleteAll`.
   * @external
   */
  bulkActionModal?: BulkActionModalRenderProp<T, F>;
}

export interface CollectionTableProps<T, F extends FiltersMap>
  extends CollectionTableBaseProps<T, F> {
  state: TableState<T, F>;
  keyedItems?: KeyedItem<T>[];
  isUsingExpandCollapse?: boolean;
}

function _CollectionTable<T, F extends FiltersMap = FiltersMap>(
  props: CollectionTableProps<T, F>,
) {
  const {
    state: tableState,
    filters,
    title,
    tabs,
    multiLevelSorting: multiLevelSortingProp,
    dataExtension,
    tags,
    views,
    extraToolbar,
    bulkActionToolbar,
    exportButton,
    exportModal,
    importModal,
    bulkActionModal,
    sticky: stickyProp,
    actionCell,
    actionCellWidth,
    actionCellProps,
    columns: columnsProp,
    onRowClick,
    onSortClick,
    showSelection: showSelectionProp,
    search,
    renderError,
    errorState = renderError &&
      (((err, { isOnline, retry }) =>
        renderError?.({
          err,
          isOnline,
          retry,
        })) as CollectionTableProps<T, F>['errorState']),
    emptyState,
    noResultsState,
    children,
    onSelectedItems,
    cardProps,
    minCardHeight,
    dataHook,
    topNotification,
    tableGridSwitchButton,
    useShowLoaderWhenEmpty,
    useNewInfiniteScrollLoader,
    dragAndDrop,
    dragAndDropBulkSubmit,
    dragAndDropSubmit,
    dragAndDropCancel,
    dragAndDropReorderModeToolbar,
    horizontalScroll: horizontalScrollProp,
    stickyColumns,
    stickySelectionColumn,
    hideBulkSelectionCheckbox,
    maxSelection,
    showTitleBar = !tableState.toolbar.container.isPanelLayout,
    BodyElementType,
    isApplyColumnWidthStyle,
    rowVerticalPadding,
    keyedItems,
    resizeProps,
    forceResize,
    showAddFieldButton,
    ...rest
  } = props;

  const state = tableState.toolbar;

  const horizontalScroll =
    horizontalScrollProp ??
    state.container.internalExperiments?.enabled(
      'specs.cairo.HorizontalScrollAsDefault',
    );

  const { scrollableContentRef } = useScrollableContentContext();

  const sticky = stickyProp ?? scrollableContentRef?.current != null;

  const customColumns = props.customColumns;

  const isMobile = useIsMobile();

  const showSelection = isMobile
    ? bulkActionToolbar != null
    : showSelectionProp ?? bulkActionToolbar != null;

  const isVirtualized = !!BodyElementType;

  useTableCollectionSyncProps({
    ...props,
    dataExtension,
    showSelection,
    customColumns,
    tags,
    isUsingTableVirtualization: isVirtualized,
  });

  const { collection, internalScroll } = state;

  const { selectedOrderedColumnsOrAll } = state;

  const { bulkSelect, query } = collection;

  const { isResizable, tableResizeProps } = useCollectionTableResize({
    columns: columnsProp,
    userResizeProps: resizeProps,
    horizontalScroll,
    columnSizesState: state.columnSizesState,
    isVirtualized,
    forceResize,
  });

  const createWSRColumnBase = useCreateWSRColumnBase({
    horizontalScroll,
    showFieldTypeIcons: state.showFieldTypeIcons,
    showAddFieldButton,
  });

  const transformedColumns: WSRTableColumn<KeyedItem<T>>[] =
    selectedOrderedColumnsOrAll.map((column, index, { length }) => {
      const {
        render,
        id,
        sortable,
        infoTooltipProps,
        title,
        width,
        style,
        ...restColumn
      } = column;

      const columnBase = createWSRColumnBase(column, index, length);

      const resizeProps = isResizable ? { id: column.id } : undefined;

      return {
        ...restColumn,
        ...columnBase,
        resizeProps,
        ...(isApplyColumnWidthStyle ? undefined : { style, width }),
        sortDescending:
          sortable && id ? query.sort.getIsSortDescending(id) : undefined,
        render: (keyedItem: KeyedItem<T>, rowNum: number) => {
          const renderedContent = render(keyedItem.item, rowNum);
          return column.searchable ? (
            <SearchableCollectionContent>
              {renderedContent}
            </SearchableCollectionContent>
          ) : (
            renderedContent
          );
        },
      };
    });

  const actionCellColumn = useActionCellColumn({
    actionCellProps: {
      ...(actionCellProps ?? {}),
      stickyActionCell: horizontalScroll || actionCellProps?.stickyActionCell,
    },
    actionCellWidth,
    collection,
    sticky,
    actionCell,
  });

  const columnsWithActions: WSRTableColumn<KeyedItem<T>>[] = actionCellColumn
    ? [...transformedColumns, actionCellColumn]
    : transformedColumns;

  const internalOnSortClick = (col: TableColumn<F>, index: number) => {
    onSortClick?.(col, index);

    if (col.id != null) {
      state.collection.sort(
        { id: col.id, sortMode: col.sortMode },
        {
          clearResult: true,
          multiple: multiLevelSortingProp !== undefined,
        },
      );
    }
  };

  const { st: tableRowSt, classes: tableRowClasses } = Table.dataTableRowStyle;
  let table = (
    <CollectionTableWSRTable
      rowClass={st(classes.root, { mobile: isMobile })}
      {...props}
      topNotification={
        typeof topNotification === 'function'
          ? () => topNotification({ query: collection.result.originQuery })
          : topNotification
      }
      customColumns={customColumns}
      rowVerticalPadding={isMobile ? 'medium' : rowVerticalPadding}
      dragAndDropReorderModeToolbar={dragAndDropReorderModeToolbar}
      selectionToolbar={
        bulkActionToolbar && (
          <SingleCollectionBulkActionToolbar
            state={state}
            bulkActionToolbar={bulkActionToolbar}
          />
        )
      }
      dynamicRowClass={() =>
        tableRowSt(tableRowClasses.root, {
          skin: 'light',
        })
      }
      horizontalScroll={horizontalScroll}
      resizable={isResizable}
      resizeProps={tableResizeProps}
      minCardHeight={minCardHeight}
      sticky={sticky}
      showTitleBar={showTitleBar}
      state={tableState}
      columns={columnsWithActions}
      onSortClick={internalOnSortClick}
      showSelection={showSelection}
      onSelectionChanged={(_selectedIds, _change) => {
        state.onSelectionChanged(_selectedIds, _change);
        onSelectedItems?.(bulkSelect.allSelected, bulkSelect.selectedValues);
        rest.onSelectionChanged?.(_selectedIds, _change);
      }}
      selectionDisabled={(row) => state.bulk.isRowCheckboxDisabled(row)}
      onRowClick={onRowClick && (({ item }, index) => onRowClick(item, index))}
      isApplyColumnWidthStyle={isApplyColumnWidthStyle}
      keyedItems={keyedItems}
    >
      <TablePlaceholderStates
        state={tableState}
        errorState={errorState}
        rowVerticalPadding={rowVerticalPadding}
        showTitleBar={showTitleBar}
        emptyState={emptyState}
        noResultsState={
          typeof noResultsState === 'function'
            ? () => {
                const {
                  result: { originQuery, hasAvailableItems },
                } = collection;
                return noResultsState({
                  hasAvailableItems,
                  query: originQuery,
                });
              }
            : noResultsState
        }
      />

      <CollectionTableContent state={tableState}>
        {useNewInfiniteScrollLoader || internalScroll ? (
          <PageInfiniteScrollLoader
            dataHook="infinite-scroll-loader"
            state={collection}
            scrollMoreAlways
          />
        ) : undefined}
        {children}
      </CollectionTableContent>
    </CollectionTableWSRTable>
  );

  if (dragAndDrop) {
    const { TableDragAndDropContext } = dragAndDrop;
    table = (
      <TableDragAndDropContext
        state={state}
        onSubmit={dragAndDropSubmit}
        onBulkSubmit={dragAndDropBulkSubmit}
      >
        {table}
      </TableDragAndDropContext>
    );
  }

  return (
    <CollectionCard
      errorState={errorState}
      state={state}
      dataHook={dataHook}
      tags={tags}
      {...cardProps}
    >
      {exportModal ?? state.featuredComponents.exportModal.value}
      {importModal ?? state.featuredComponents.importModal.value}
      {bulkActionModal && (
        <SingleCollectionBulkActionModal
          state={state}
          bulkActionModal={bulkActionModal}
        />
      )}
      {table}
    </CollectionCard>
  );
}

export const CollectionTable = observer(_CollectionTable);
