import React, {
  ReactElement,
  useEffect,
  useLayoutEffect,
  useMemo,
  useState,
} from 'react';
import { Box, DropdownProps, TabsProps } from '@wix/design-system';
import { Filter } from '@wix/bex-core';
import { runInAction } from 'mobx';
import { FilterContextProvider } from '../FilterContext';
import { observer } from 'mobx-react-lite';
import { TabTitle } from './TabTitle';
import { useToolbarCollectionContext } from '../ToolbarCollectionContext';
import { TabsFilterState } from './TabsFilterState';
import { TabsDropdown } from './TabsDropdown';
import { ALL_TAB_ITEM_ID, TabsFilterItemBase } from './types';
import { TabsDefault } from './TabsDefault';
import { useFilterValueTotalState } from '../TabTotal/useFilterValueTotalState';
import type { TabCounterBadgeProps } from '../TabTotal/TabTotalCounterBadge';
import type { TabTotalParensProps } from '../TabTotal/TabTotalParens';

/** Type alias for TabTotalCounterBadge component element */
type TabTotalCounterBadge<V> = ReactElement<
  TabCounterBadgeProps<V, unknown, any>
>;
/** Type alias for TabTotalParens component element */
type TabTotalParens<V> = ReactElement<TabTotalParensProps<V, unknown, any>>;

export interface TabsFilterProps<V> {
  /**
   * List of tabs.
   * @external
   */
  data: V[];
  /**
   * Defines the selected filter.
   * <br><br>
   * Must be an [arrayFilter](./?path=/story/features-filter-factories--arrayfilter) object.
   * @external
   */
  filter: Filter<V[]>;
  /**
   * Display name of the tab that indicates that all items are included.
   * <br>
   * Changes the value of `filter` to an empty array.
   * @external
   */
  all?: string | null | false;

  /**
   * Renders a counter badge next to the tabs' texts.
   * <br><br>
   * See different examples of this implementation in the [Overview](./?path=/story/features-filter-components--tabsfilter) tab.
   * @external
   */
  counterBadge?: (
    value: V | null | undefined,
  ) => TabTotalCounterBadge<V> | TabTotalParens<V> | null | undefined;
  onChange?: (value: V | null | undefined) => unknown;

  /**
   * Tabs layout type.
   * <br><br>
   * If this prop is omitted, the width depends on the label width. The total width of all the tabs cannot exceed 80% of the toolbar.
   * <br><br>
   * Supported values:<br>
   * - `uniformFull`: Tabs are spread out uniformly.
   * @external
   */
  type?: Extract<TabsProps['type'], 'uniformFull'>;

  placeAllTabLast?: boolean;

  /**
   * Custom popover props for the mobile dropdown menu. On mobile devices, tabs are displayed as a dropdown menu.
   * @external
   */
  mobilePopoverProps?: DropdownProps['popoverProps'];
}

function _TabsFilter<V>(props: TabsFilterProps<V>) {
  const {
    type,
    filter,
    data: dataProp,
    all,
    counterBadge,
    placeAllTabLast,
    onChange,
    mobilePopoverProps,
  } = props;
  const { itemKey, itemName } = filter;

  const table = useToolbarCollectionContext();
  const filterAllValueTotal = useFilterValueTotalState({
    ...props,
    value: undefined,
  });

  const container = React.useRef<HTMLDivElement>();

  const [state] = useState(
    () =>
      (table.tabs ??= new TabsFilterState({
        table,
      })),
  );

  useLayoutEffect(() => {
    runInAction(() => {
      if (filter.persistent == null) {
        filter.persistent = true;
      }
    });
  }, []);

  const { responsive } = state;

  const isAllCounterBadgeLoading = useMemo(() => {
    const allStatus = filterAllValueTotal.totalsCollection.initTask.status;
    const { isLoading, isIdle } = allStatus;
    return isLoading || isIdle;
  }, [filterAllValueTotal.totalsCollection.initTask.status]);

  const isTableLoading = useMemo(() => {
    const { isLoading, isIdle } = table.initTask.status;
    return isLoading || isIdle;
  }, [table.initTask.status]);

  const hideCounterBadge = isTableLoading || isAllCounterBadgeLoading;

  const selectedId = filter.isEmpty
    ? ALL_TAB_ITEM_ID
    : itemKey(filter.value[0]);

  const items = useMemo<TabsFilterItemBase[]>(() => {
    const _mergedItems = dataProp.map((value, index) => {
      const id = itemKey(value);
      return {
        id,
        name: itemName(value),
        title: (
          <TabTitle
            tabIndex={all && !placeAllTabLast ? index + 1 : index}
            state={responsive}
            text={itemName(value)}
            value={value}
            active={selectedId === id}
            counterBadge={hideCounterBadge ? undefined : counterBadge}
          />
        ),
        value,
      };
    });

    if (!all) {
      return _mergedItems.map((item, index) => ({
        ...item,
        index,
        minWidth: responsive.itemsMinWidths[index],
        maxWidth: responsive.itemMaxWidth,
      }));
    }

    const allTabItem = {
      id: ALL_TAB_ITEM_ID,
      name: all,
      title: (
        <TabTitle
          tabIndex={0}
          state={responsive}
          text={all}
          value={undefined}
          active={selectedId === ALL_TAB_ITEM_ID}
          counterBadge={hideCounterBadge ? undefined : counterBadge}
        />
      ),
    };

    return (
      placeAllTabLast
        ? [..._mergedItems, allTabItem]
        : [allTabItem, ..._mergedItems]
    ).map((item, index) => ({
      ...item,
      minWidth: responsive.itemsMinWidths[index],
      index,
    }));
  }, [
    dataProp,
    all,
    counterBadge,
    responsive.panelMinWidth,
    hideCounterBadge,
    selectedId,
  ]);

  useEffect(
    () =>
      state.init({
        container: container.current,
        itemsCount: items.length,
      }),
    [container, state],
  );

  return (
    <FilterContextProvider value={filter}>
      <Box
        ref={(node) => {
          if (node) {
            container.current = node;
          }
        }}
        dataHook="collection-tabs-filter"
        width="100%"
        height={60}
        direction="vertical"
        verticalAlign="middle"
        flexGrow={1}
      >
        {responsive.mode === 'tabs' ? (
          <TabsDefault
            items={items}
            data={dataProp}
            filter={filter}
            selectedId={selectedId}
            state={state}
            onChange={onChange}
            type={type}
            all={all}
          />
        ) : (
          <TabsDropdown
            filter={filter}
            data={dataProp}
            items={items}
            selectedId={selectedId}
            popoverProps={mobilePopoverProps}
          />
        )}
      </Box>
    </FilterContextProvider>
  );
}

export const TabsFilter = observer(_TabsFilter);
TabsFilter.displayName = 'TabsFilter';
