import { Box, CardProps, usePageScrollContext } from '@wix/design-system';
import React, {
  CSSProperties,
  ReactElement,
  ReactNode,
  useEffect,
  useState,
} from 'react';
import type {
  CollectionToolbarProps,
  FilterElement,
  TabsFilterProps,
} from '../..';
import { ComputedQuery, FiltersMap } from '@wix/bex-core';
import { ToolbarCollectionState } from '../../state';
import {
  assertComponentType,
  CollectionSearchElement,
  CollectionToolbarFiltersElement,
  CollectionViewsDropdownElement,
  CustomColumnsElement,
  PrimaryActionButtonElement,
  ReorderModeToolbarElement,
  TabsFilterElement,
  ToolbarTitleElement,
  TopNotificationElement,
  ToolbarSecondaryActionsElement,
} from '../assertComponentType';
import { CollectionToolbars } from '../CollectionToolbars';
import {
  DragAndDropBulkSubmit,
  DragAndDropCancel,
  DragAndDropSubmit,
} from '../DragAndDrop';
import { TableGridSwitchButtonProps } from '../TableGridSwitchButton';
import { checkConflictingToolbarProps } from './checkConflictingToolbarProps';
import type { AddApplyFiltersButtonType } from '../FiltersPanel/AddApplyFiltersButton';
import type { TagsElement } from '../Tags';
import type { ExtensionMenuItemsType } from '../ExtensionMenuItems';
import { TablePlaceholderStatesProps } from '../TablePlaceholderStates/TablePlaceholderStates';
import { DragAndDropCategoriesProps } from '../DragAndDrop/DragAndDropCategoriesProps';
import { MoreActionsElement } from '../MoreActions';

export interface ToolbarBaseProps {
  /**
   * Displays a title at the left of a toolbar. Accepts a [ToolbarTitle](./?path=/story/features-display-toolbar--toolbartitle) component.
   * @overrideType [ToolbarTitle](./?path=/story/features-display-toolbar--toolbartitle)
   * @external
   */
  title?: ToolbarTitleElement;
  /**
   * Displays a search input on the right side of the table toolbar.<br> <br>
   * Accepts a boolean or a custom React element as a parameter. When passing `false`, no search input is displayed.
   * @default true
   * @external
   */
  search?: boolean | FilterElement | CollectionSearchElement;

  /**
   * Indicates whether to show Table Title Bar or not.
   * Note: if you pass `showTitleBar = false` title bar is not removed from the DOM. For better accessibility column titles should be defined as well.
   * @default true
   * @external
   */
  showTitleBar?: boolean;
  /**
   * A [`<ExportButton />`](./?path=/story/features-export--exportto) react element to be added to the toolbar
   */
  exportButton?: ReactElement;
  /**
   * A [`<ImportButton />`] react element to be added to the toolbar.
   */
  importButton?: ReactElement;

  /**
   * A [`<ExportModal />`](./?path=/story/features-export--exportto#CTA_is_out_of_the_table) react element to be rendered in table's context.
   */
  exportModal?: ReactElement;
  /**
   * A [`<ImportModal />`] react element to be rendered in table's context.
   */
  importModal?: ReactElement;
}

export interface ToolbarCollectionBaseProps<T, F extends FiltersMap>
  extends ToolbarBaseProps,
    DragAndDropCategoriesProps<F> {
  children?: ReactNode | ReactNode[];

  /**
   * Adds view options to the table headers.
   * Accepts a [Views](./?path=/story/features-display-views--views) component.
   * @overrideType [Views](./?path=/story/features-display-views--views)
   * @external
   */
  views?: CollectionViewsDropdownElement<F>;
  /**
   * Adds filters to the toolbar and/or a sliding panel.
   * Accepts a `ToolbarFilters` component, which must have the `panelTitle` prop defined.
   * @overrideType [ToolbarFilters](./?path=%2Fstory%2Ffeatures-filter-components--toolbarfilters)
   * @external
   */
  filters?: CollectionToolbarFiltersElement;
  /**
   * Displays tabs at the left of a toolbar.
   * Accepts a [TabsFilter](./?path=/story/features-filter-components--tabsfilter) component.
   * @overrideType [TabsFilter](./?path=/story/features-filter-components--tabsfilter)
   * @external
   */
  tabs?: TabsFilterElement<TabsFilterProps<T>>;

  selectionToolbar?: ReactElement;

  sticky?: boolean;
  /**
   * Minimum height for a card's container when showing:<br>
   * - A loader<br>
   * - Empty state<br>
   * - Error state
   * <br><br>
   * If no unit is specified, `px` is used.
   * @default 300px
   * @external
   */
  minCardHeight?: CSSProperties['minHeight'];

  /**
   * A render function to be rendered when there's an error fetching data from the server.<br>
   * The function accepts the following parameters <br>
   *   * `err`: The error thrown by the [fetchData](./?path=/story/common-hooks--usecollection) function <br>
   *   * `isOnline`: Indicates whether internet connection is available <br>
   *   * `retry`: Retry collection fetch on failed response, similar to `collection.retryFetch` <br>
   * Returns [`EmptyState`](https://www.docs.wixdesignsystem.com/?path=/story/components-layout--emptystate) component.
   * @external
   * @deprecated Use `errorState` instead.
   */
  renderError?: TablePlaceholderStatesProps['renderError'];

  /**
   * A render function to be rendered when there's an error fetching data from the server.<br>
   * The function accepts the following parameters <br>
   *   * `err`: The error thrown by the [fetchData](./?path=/story/common-hooks--usecollection) function <br>
   *   * `isOnline`: Indicates whether internet connection is available <br>
   *   * `retry`: Retry collection fetch on failed response, similar to `collection.retryFetch` <br>
   * Returns [`EmptyState`](https://www.docs.wixdesignsystem.com/?path=/story/components-layout--emptystate) component.
   * @external
   */
  errorState?: TablePlaceholderStatesProps['errorState'];

  /**
   * Renders when there are no items to display. Accepts a [`CollectionEmptyState`](./?path=/story/features-display-empty-states--collectionemptystate) component.
   * @external
   * @overrideType [CollectionEmptyState](./?path=/story/features-display-empty-states--collectionemptystate)
   */
  emptyState?: ReactNode;

  /**
   * Shown when a search or filter has no results.
   * Accepts a [`CollectionNoResultsState`](./?path=/story/features-display-empty-states--collectionnoresultsstate) component,
   * or a function that accepts the following parameters:
   * - `hasAvailableItems`: Indicates whether other items may be shown using other filter or search parameters.
   * - `query`: An instance of [`ComputedQuery`](./?path=/story/common-models--computedquery) representing the query that resulted in the empty state.
   * The function should return [`CollectionNoResultsState`](./?path=/story/features-display-empty-states--collectionnoresultsstate).
   * @external
   * @overrideType [CollectionNoResultsState](./?path=/story/features-display-empty-states--collectionnoresultsstate)
   */
  noResultsState?:
    | ReactNode
    | ((params: {
        hasAvailableItems: boolean;
        query: ComputedQuery<F>;
      }) => ReactNode);

  cardProps?: Partial<CardProps>;
  onSelectedItems?: (allSelected: boolean, items: T[]) => unknown;
  /**
   * Renders a notification below the header. Accepts a [`TableTopNotification`](./?path=/story/features-display-tabletopnotification--tabletopnotification) component.
   * @overrideType [TableTopNotification](./?path=/story/features-display-tabletopnotification--tabletopnotification)
   * @external
   */
  topNotification?:
    | TopNotificationElement
    | ((params: {
        query: ComputedQuery<F>;
      }) => TopNotificationElement | boolean | null | undefined)
    | boolean
    | null
    | undefined;

  extraToolbar?: ReactNode;

  /**
   * Display a summary bar below the toolbar.
   * Accepts a [`SummaryBar`](./?path=/story/features-display-toolbar--summarybar) component.
   * @overrideType [SummaryBar](./?path=/story/features-display-toolbar--summarybar)
   * @external
   */
  summaryBar?: ReactNode;
  tableGridSwitchButton?: ReactElement<TableGridSwitchButtonProps>;

  /**
   * Function that submits a drop event to the server. <br> <br>
   * The parameter is an event object containing the following details: <br>
   * -`from: {item: T; index: number}`: The dragged item. <br>
   * -`after: null | [item: T; iindex: number}`: The previous dropped item. If this is the first item to be dropped, this argument is `null`. <br>
   * -`filters`: Applied [Drag and Drop](./?path=/story/features-sort-drag-and-drop--overview) filters.
   *
   * @returns Promise
   * @external
   */
  dragAndDropSubmit?: DragAndDropSubmit<T, F>;

  /**
   * Function that submits an array of drop events to the server. <br> <br>
   * The parameter is an array of event objects containing the following details: <br>
   * -`from: {item: T; index: number}`: The dragged item. <br>
   * -`after: null | [item: T; index: number}`: The previous dropped item. If this is the first item to be dropped, this argument is `null`. <br>
   * -`filters`: Applied [Drag and Drop](./?path=/story/features-sort-drag-and-drop--overview) filters. <br>
   *
   * @returns Promise
   * @external
   */
  dragAndDropBulkSubmit?: DragAndDropBulkSubmit<T, F>;

  /**
   * A function that cancels a drop event. <br>
   * Supported arguments: <br>
   * - `from: {item: T; index: number}`: The dragged item.
   * - `after: null | {item: T; index: number}`: The item that the dragged item was dropped after. If dropped at the head of the collection, this argument will be `null`.
   * - `filters`: Currently applied filters. Used in cases where your server holds a unique ordering per filtering. See the `dragAndDropCategories` prop.
   *
   * If the move is cancelled, the function returns a toast config object with a message. Otherwise, the function will return a null value.
   * @external
   */
  dragAndDropCancel?: DragAndDropCancel<T, F>;

  /**
   * @deprecated It doesn't need to be passed implicitly anymore.
   */
  extensionMenuItems?: ExtensionMenuItemsType;

  /**
   * Drag and drop reorder mode toolbar. This prop accepts a `ReorderModeToolbar` component.<br>
   * The `ReorderModeToolbar` component accepts the following props:<br>
   *  + `learnMoreUrl`: [string] A URL in the toolbar.<br>
   *  + `reorderModeTitle`: [string] A title in the toolbar.
   * @external
   */
  dragAndDropReorderModeToolbar?: ReorderModeToolbarElement | null;

  /**
   * Adds functionality to allow visitors to create choose which columns to display and in what order. Accepts a [CustomColumns](./?path=/story/features-display--customcolumns) component.
   * @overrideType [CustomColumns](./?path=/story/features-display--customcolumns)
   * @external
   */
  customColumns?: CustomColumnsElement | null;

  /**
   * Adds functionality to allow visitors to sort multiple columns simultaneously.
   * Accepts a [MultiLevelSorting](./?path=/story/features-sort-sortable-columns-multilevelsorting--multilevelsorting) component.
   * @overrideType [MultiLevelSorting](./?path=/story/features-sort-sortable-columns-multilevelsorting--multilevelsorting)
   * @external
   */
  multiLevelSorting?: CollectionToolbarProps<T, F>['multiLevelSorting'];

  /**
   * Adds functionality for a primary action button in the toolbar.
   * Accepts a [PrimaryActionButton](./?path=/story/features-actions-primary-actions--primary-action-button) component.
   * @external
   * @overrideType [PrimaryActionButton](./?path=/story/features-actions-primary-actions--primary-action-button)
   */
  primaryActionButton?: PrimaryActionButtonElement;

  /**
   * Secondary buttons w/o PopoverMenu component to be rendered on the toolbar.
   * @overrideType [ToolbarSecondaryActions](./?path=/story/features-actions-secondary-actions--toolbar-secondary-actions)
   * @external
   */
  secondaryActions?: ToolbarSecondaryActionsElement;

  /**
   * A [`MoreActions`](./?path=/story/features-actions-more-actions--more-actions) component to be rendered on the header.
   * @overrideType [MoreActions](./?path=/story/features-actions-more-actions--more-actions)
   * @external
   */
  moreActions?: MoreActionsElement;

  /**
   * Apply filter button implementation: `import { AddApplyFiltersButton } from '@wix/patterns';`
   * <br>
   * Add `Apply` button to the filters panel footer. Panel filters will be applied only after the button is clicked.
   */
  AddApplyFiltersButton?: AddApplyFiltersButtonType;

  /**
   * Limits the amount of rows a visitor can select.
   * @external
   */
  maxSelection?: number;

  /**
   * Indicates whether the table should have an [internal scroll](./?path=/story/features-display-internal-scroll--internal-scroll) instead of the default page scroll.
   * @external
   */
  internalScroll?: boolean;

  /**
   * Enables tags feature. In this element you can pass the configuration
   * @overrideType [TagsElement](./?path=/story/features-enrich-tags--tags)
   */
  tags?: TagsElement<T, F>;

  /**
   * When true, renders prefix icons for field types in column headers, filters, and the customize-columns panel.
   * Each column and filter should have a `fieldType` property set so Cairo can render the matching icon.
   * @default false
   * @external
   */
  showFieldTypeIcons?: boolean;
}

export interface ToolbarCollectionProps<T, F extends FiltersMap>
  extends ToolbarCollectionBaseProps<T, F> {
  state: ToolbarCollectionState<T, F>;
}

export function ToolbarCollection<T, F extends FiltersMap>(
  props: ToolbarCollectionProps<T, F>,
) {
  const {
    state,
    filters,
    title,
    tabs,
    views,
    extraToolbar,
    selectionToolbar,
    dragAndDropReorderModeToolbar,
    sticky: stickyProp,
    search,
    children,
    tableGridSwitchButton,
    exportButton,
    importButton,
    primaryActionButton,
    secondaryActions,
    moreActions,
    summaryBar,
    topNotification,
  } = props;
  assertComponentType(views, 'CollectionViewsDropdown', 'views');
  checkConflictingToolbarProps(props);

  const { scrollableContentRef: scrollablePageRef } = usePageScrollContext();

  const [sticky, setSticky] = useState(
    stickyProp ?? scrollablePageRef?.current != null,
  );

  useEffect(() => {
    setSticky(stickyProp ?? scrollablePageRef?.current != null);
  }, [stickyProp]);

  return (
    <>
      <CollectionToolbars
        sticky={sticky}
        state={state}
        search={search}
        views={views}
        title={title}
        tabs={tabs}
        filters={filters}
        selectionToolbar={selectionToolbar}
        dragAndDropReorderModeToolbar={dragAndDropReorderModeToolbar}
        extraToolbar={extraToolbar}
        tableGridSwitchButton={tableGridSwitchButton}
        exportButton={exportButton}
        importButton={importButton}
        primaryActionButton={primaryActionButton}
        secondaryActions={secondaryActions}
        moreActions={moreActions}
        summaryBar={summaryBar}
        topNotification={
          typeof topNotification === 'function'
            ? () =>
                topNotification({ query: state.collection.result.originQuery })
            : topNotification
        }
        titleBar={
          <Box
            width="100%"
            borderTopColor="D60"
            borderTopWidth="1px"
            borderTopStyle="solid"
            boxSizing="border-box"
          />
        }
      />
      <Box
        dataHook="toolbar-collection-content"
        width="100%"
        direction="vertical"
        minHeight={0}
        flex={1}
        verticalAlign="middle"
      >
        {children}
      </Box>
    </>
  );
}
