import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { observer } from 'mobx-react-lite';
import { CustomModalContent, PickerModalState } from '../../hooks';
import { ComputedQuery, FiltersMap } from '@wix/bex-core';
import {
  Box,
  FloatingNotification,
  FloatingNotificationProps,
  IconElement,
  Text,
  TextButton,
} from '@wix/design-system';
import { CollectionSearch } from '../CollectionSearch';
import { Picker, PickerProps } from '../Picker';
import { CollectionProvider } from '../CollectionContext';
import {
  BulkSelectionMaxSelectionLabel,
  MaxSelectionLabelProps,
  MultipleSelection,
  reachedMaxSelection,
  showMaxSelectionLabel,
} from '../MaxSelection';
import { PickerContentState } from '../../state';
import { MaybeTooltip } from '../MaybeTooltip';
import { PickerListItemProps } from '../PickerListItemProps';
import { SelectAllCheckbox } from './SelectAllCheckbox';
import { MultiCollectionSupportProvider } from '../MultiCollectionSupportProvider';
import {
  PickerContentBaseWrapped,
  PickerContentBaseWrappedProps,
} from './PickerContentBaseWrapped';
import { FilterRenderingContextProvider } from '../FilterRenderingContext';
import { CollectionSearchElement } from '../assertComponentType';
import { isValidElement } from '../isValidElement';

export interface PickerContentItemsSelectionProps<T, F extends FiltersMap>
  extends Partial<PickerContentBaseWrappedProps> {
  modalState: PickerModalState<T, F>;
  customContentButtonText?: string;
  customContentButtonPrefixIcon?: IconElement;

  /**
   * A callback to be called when the custom content button is clicked
   */
  customContentButtonOnClick?: (param: {
    onClose: (newItems?: T[]) => void;
  }) => void;

  customContent?: CustomModalContent<T>;
  /**
   * Shows "Select All" checkbox as a side action (overrides "customContent" prop).
   * If `multiple={number}` or `multiple={false}` are used, this prop does nothing.
   */
  showSelectAllCheckbox?: boolean;

  multiple?: MultipleSelection;

  /**
   * Shows a SingleSelectFilter component in a picker modal to filter items;
   */
  filterComponent?: ReactElement;
  /**
   * A function that render a item row in the selector list
   */
  renderItem: (item: T, index: number) => Partial<PickerListItemProps>;
  /**
   * A render function to be rendered when there're no items to show in the table.<br>
   * You can use the built in [`<CollectionEmptyState />`](./?path=/story/features-display-empty-states--collectionemptystate).<br>
   * The function accepts the following parameters
   *   * `hasAvailableItems`: Indicates whether other items might have been shown using other filtering / search
   *   * `query`: An instance of [ComputedQuery](./?path=/story/common-types--computedquery) that represents the query that resulted with an empty state
   */
  renderEmptyState?: (params: {
    hasAvailableItems: boolean;
    query: ComputedQuery<F>;
  }) => ReactElement<any, any> | null | undefined;
  renderError: PickerProps<T, F>['renderError'];
  /**
   * When set to `true`, the modal primary button will be always enabled.
   */
  enablePrimaryButton?: boolean;
  /**
   * Displays a search input in the picker modal.
   * Accepts a boolean or a custom CollectionSearch element as a parameter.
   * When passing `false`, no search input is displayed.
   * @default true
   * @external
   */
  search?: boolean | CollectionSearchElement;
  /**
   * @deprecated Use search={false} instead
   * Hides the search input of the picker
   */
  hideSearch?: boolean;
  /**
   * Function that returns label for multi selection like:
   * '2 items selected (24 max)`
   * This function accepts argument {num: number, max: number}
   */
  customMaxSelectionLabel?: MaxSelectionLabelProps['customLabel'];
  /**
   * Function that returns custom notification that will be displayed in the picker modal.
   * Useful to describe additional information about the selection (for instance how to increase limit of selected items).
   * @param data.selected - number of selected items
   * @param data.total - total number of items
   */
  renderCustomNotification?: (data: {
    selected?: number;
    total?: number;
  }) => ReactElement<FloatingNotificationProps, typeof FloatingNotification>;
  /** Displays count of selected items in <CustomModalLayout /> `sideActions` */
  showSelectedCount?: boolean;
}

interface PickerContentItemsSelectionStateProps<T, F extends FiltersMap> {
  state: PickerContentState<T, F>;
  renderPickerContent: (params: {
    state: PickerContentState<T, F>;
    renderItem: PickerContentItemsSelectionProps<T, F>['renderItem'];
  }) => ReactElement;
}

export type PickerContentItemsSelectionFullProps<
  T,
  F extends FiltersMap = FiltersMap,
> = PickerContentItemsSelectionProps<T, F> &
  PickerContentItemsSelectionStateProps<T, F>;

function _PickerContentItemsSelection<T, F extends FiltersMap = FiltersMap>(
  props: PickerContentItemsSelectionFullProps<T, F>,
) {
  const {
    modalState,
    primaryButtonProps,
    renderEmptyState,
    renderError,
    state,
    multiple,
    customContentButtonText,
    customContentButtonPrefixIcon,
    customContentButtonOnClick,
    customContent,
    showSelectAllCheckbox,
    enablePrimaryButton,
    search,
    hideSearch,
    renderPickerContent,
    customMaxSelectionLabel,
    renderCustomNotification,
    filterComponent,
    showSelectedCount,
    ...rest
  } = props;
  const { _customContent } = modalState;

  const { picker } = state;

  const { collection } = picker;

  const { bulkSelect, translate: t, showInitialLoader } = collection;

  const { isEmpty: isSelectEmpty } = bulkSelect;

  useEffect(() => {
    _customContent.value = () => customContent;
  }, []);

  const reachedMax = reachedMaxSelection({
    collection,
    multiple,
  });

  const [filterRenderingContextValue] = useState(() => ({
    location: 'modal' as const,
  }));
  const onCustomContentButtonClick = useCallback(() => {
    state.onCreateNewButtonClick(customContentButtonOnClick);
  }, [customContentButtonOnClick]);

  const renderItem: (typeof props)['renderItem'] = (item, index) => {
    const renderedItem = props.renderItem(item, index);
    const key = collection.itemKey(item);
    return {
      ...renderedItem,
      disabled:
        (reachedMax && !collection.bulkSelect.selectedIds.includes(key)) ||
        renderedItem.disabled,
    };
  };

  const searchElement = isValidElement(search) ? (
    React.cloneElement(search as CollectionSearchElement, {
      minWidthPixels: '100%',
    })
  ) : search !== false ? (
    <CollectionSearch minWidthPixels="100%" />
  ) : null;

  const shouldShowSearch =
    search !== undefined ? search !== false : !hideSearch;
  const resolvedSearchElement = shouldShowSearch ? searchElement : null;

  const renderSideActions = () => {
    if (
      showSelectAllCheckbox &&
      !showInitialLoader &&
      (multiple === true || multiple === Infinity)
    ) {
      return <SelectAllCheckbox state={state} />;
    }

    const shouldShowCustomContentButton =
      customContent || customContentButtonOnClick;

    if (shouldShowCustomContentButton) {
      return (
        <Box>
          <MaybeTooltip
            dataHook="custom-content-button-tooltip"
            content={reachedMax && t('cairo.picker.createNew.button.tooltip')}
          >
            <div>
              <TextButton
                dataHook="custom-content-button"
                prefixIcon={customContentButtonPrefixIcon}
                disabled={reachedMax}
                onClick={onCustomContentButtonClick}
              >
                {customContentButtonText ?? t('cairo.picker.createNew.button')}
              </TextButton>
            </div>
          </MaybeTooltip>
        </Box>
      );
    }
    if (showSelectedCount) {
      return (
        <Text dataHook="cairo-selected-items-count">
          {t('cairo.selector.modal.items.selected', {
            total: collection.bulkSelect.selectedCount,
          })}
        </Text>
      );
    }
    return null;
  };

  const renderFiltersComponent = () => {
    return (
      <Box gap="12px">
        <Box width="200px">
          <FilterRenderingContextProvider value={filterRenderingContextValue}>
            {filterComponent}
          </FilterRenderingContextProvider>
        </Box>
        {resolvedSearchElement && <>{resolvedSearchElement}</>}
      </Box>
    );
  };

  const showMaxSelection = showMaxSelectionLabel(multiple);
  return (
    <PickerContentBaseWrapped
      secondaryButtonText={t('cairo.cancel.button')}
      primaryButtonProps={{
        disabled: isSelectEmpty && !enablePrimaryButton,
        ...primaryButtonProps,
        style: {
          ...primaryButtonProps?.style,
        },
      }}
      sideActions={renderSideActions()}
      {...rest}
    >
      <CollectionProvider value={picker.collection}>
        {(resolvedSearchElement ||
          filterComponent ||
          renderCustomNotification ||
          showMaxSelection) && (
          <Box
            display="grid"
            paddingLeft="SP5"
            paddingRight="SP5"
            paddingBottom="SP3"
            paddingTop={rest.showHeaderDivider ? 'SP3' : '3px'}
            gap="SP3"
          >
            {filterComponent ? renderFiltersComponent() : resolvedSearchElement}
            {renderCustomNotification ? (
              <span data-hook="custom-notification">
                {renderCustomNotification({
                  selected: collection.bulkSelect.selectedCount,
                  total: collection.result.total,
                })}
              </span>
            ) : null}
            {showMaxSelection && (
              <BulkSelectionMaxSelectionLabel
                dataHook="selection-max-label"
                multiple={multiple}
                customLabel={customMaxSelectionLabel}
                t={t}
                selectedCount={collection.bulkSelect.selectedCount}
              />
            )}
          </Box>
        )}
        <MultiCollectionSupportProvider value={picker.multi}>
          <Picker
            dataHook="picker"
            state={picker}
            renderEmptyState={renderEmptyState}
            renderError={renderError}
            hasFilterDropdown={!!filterComponent}
          >
            {renderPickerContent?.({ state, renderItem })}
          </Picker>
        </MultiCollectionSupportProvider>
      </CollectionProvider>
    </PickerContentBaseWrapped>
  );
}
export const PickerContentItemsSelection = observer(
  _PickerContentItemsSelection,
);
