import React, { ReactElement, useMemo, useRef } from 'react';

import { ButtonProps } from '../../Button/Button';
import { CommonProps } from '../../common';
import {
  FilterByWrapper,
  FiltersWrapper,
  StyledFilterList,
  StyledFilterListWithAddons,
  StyledFiltersHeader,
  StyledTagList,
} from './StyledFilters';
import { FiltersProvider } from './FiltersContext';
import Button from '../../Button';
import GroupedFilter from './GroupedFilter';
import Icon from '../../Icon';
import css from '../../../utils/css';
import { FilterProps } from './Filter';
import { FilterValues } from './types';

export interface FiltersProps<T extends FilterValues> extends CommonProps {
  /**
   * Props will be passed to the apply button, if this is undefined, the apply button won't be rendered
   */
  applyButtonProps?: ButtonProps;
  /**
   * List of filters
   */
  children:
    | ReactElement<FilterProps<T, keyof T>>
    | ReactElement<FilterProps<T, keyof T>>[];
  /**
   * Props will be passed to the clear button, if this is undefined or clearable prop of Filter component is set to false, the clear button won't be rendered
   */
  clearButtonProps?: ButtonProps;
  /**
   * Function to update filter value
   */
  onChange: (value: T) => void;
  /**
   * Function to call when reset filter state
   */
  onReset?: () => void;
  /**
   * Function to call when saving filter state
   */
  onSave?: (value: T) => void;
  /**
   * Filter value, each filter in children will using this value to extract its own value by key
   *
   * PossibleFilterValue is union of all possible value for all of the filter type
   */
  value: T;
}

// The number of fully display filters
// TODO: responsively adjust this number
const FILTERS_BREAKPOINT = 4;

const Filters = <T extends FilterValues>({
  value,
  onChange,
  onSave,
  onReset,
  applyButtonProps,
  clearButtonProps,
  children,
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
}: FiltersProps<T>) => {
  const selectedTagsRef = useRef<HTMLDivElement>(null);

  const childrenArray = React.Children.toArray(children);
  const displayedFilters = childrenArray.slice(0, FILTERS_BREAKPOINT);
  const groupedFilters = childrenArray.slice(FILTERS_BREAKPOINT);

  const baseProviderValue = useMemo(
    () => ({
      values: value,
      onValuesChange: onChange,
      onSave,
      applyButtonProps,
      clearButtonProps,
      selectedTagsRef,
    }),
    [value, onChange, onSave, applyButtonProps, clearButtonProps]
  );
  const unGroupedProviderValue = useMemo(
    () => ({
      ...baseProviderValue,
      isGrouped: false,
    }),
    [baseProviderValue]
  );
  const groupedProviderValue = useMemo(
    () => ({
      ...baseProviderValue,
      isGrouped: true,
    }),
    [baseProviderValue]
  );

  const filterByLabel = (
    <FilterByWrapper>
      <Icon icon="adjustment" sx={{ mr: 'small' }} />
      Filter by
    </FilterByWrapper>
  );
  const resetButton = onReset !== undefined && (
    <Button
      variant="text"
      text="Reset"
      icon="restart"
      sx={{
        fontWeight: 'semiBold',
      }}
      onClick={onReset}
    />
  );

  return (
    <FiltersProvider value={unGroupedProviderValue}>
      <FiltersWrapper
        id={id}
        className={className}
        data-test-id={dataTestId}
        style={{ ...style, ...css(sx) }}
      >
        <StyledFiltersHeader>
          {filterByLabel}
          {resetButton}
        </StyledFiltersHeader>
        <StyledFilterListWithAddons>
          {filterByLabel}
          <StyledFilterList>
            {displayedFilters}
            <FiltersProvider value={groupedProviderValue}>
              {groupedFilters.length !== 0 && (
                <GroupedFilter label="More" filters={groupedFilters} />
              )}
            </FiltersProvider>
          </StyledFilterList>
          {resetButton}
        </StyledFilterListWithAddons>
        <StyledTagList ref={selectedTagsRef} />
      </FiltersWrapper>
    </FiltersProvider>
  );
};

export default Filters;
