import { useTranslation } from 'react-i18next';
import { CloseIcon } from '../icons/CloseIcon';
import { AppliedFiltersCssClasses } from './AppliedFilters';
import { useClearFiltersCallback } from '../hooks/useClearFiltersCallback';
import { FieldValueFilter, useSearchActions } from '@yext/search-headless-react';
import { isDuplicateFieldValueFilter } from '../utils/filterutils';
import { executeSearch } from '../utils';
import React, { useCallback, useMemo } from 'react';

/**
 * A representation of a filter that can be removed from the AppliedFilters component.
 *
 * @internal
 */
export interface RemovableFilter {
  displayName: string,
  handleRemove: () => void,
  filter: FieldValueFilter
}

/**
 * Properties for {@link AppliedFilters}.
 *
 * @internal
 */
export interface AppliedFiltersDisplayProps {
  removableFilters?: RemovableFilter[],
  /**
   * The display values of filters that are applied to the search results
   * from the backend's natural language processing.
   */
  nlpFilterDisplayNames?: string[],
  /** CSS classes for customizing the component styling. */
  cssClasses?: AppliedFiltersCssClasses
}

/**
 * A component that renders applied filters based on the provided GroupedFilters.
 *
 * @param props - {@link AppliedFiltersDisplayProps}
 * @returns A React element for the applied filters
 */
export function AppliedFiltersDisplay(props: AppliedFiltersDisplayProps): React.JSX.Element | null {
  const { t } = useTranslation();
  const {
    nlpFilterDisplayNames = [],
    removableFilters = [],
    cssClasses = {}
  } = props;
  const handleClickClearAllButton = useClearFiltersCallback();
  const searchActions = useSearchActions();

  const dedupedNlpFilterDisplayNames = nlpFilterDisplayNames.filter(displayName => {
    return !removableFilters.some(f => f.displayName === displayName);
  });

  const dedupedRemovableFilters = getDedupedRemovableFilters(removableFilters);
  const handleRemoveDedupedFilter = useCallback((dedupedFilter: DedupedRemovableFilter) => {
    dedupedFilter.handleRemove();
    for (const f of dedupedFilter.duplicates ?? []) {
      f.handleRemove();
    }
    searchActions.setOffset(0);
    executeSearch(searchActions);
  }, [searchActions]);
  const removableFiltersWithHandlers = useMemo(() => {
    return dedupedRemovableFilters.map(filter => ({
      filter,
      handleRemove: () => handleRemoveDedupedFilter(filter)
    }));
  }, [dedupedRemovableFilters, handleRemoveDedupedFilter]);

  if (removableFilters.length + nlpFilterDisplayNames.length === 0) {
    return null;
  }

  return (
    <div className={cssClasses.appliedFiltersContainer} aria-label={t('appliedFiltersToCurrentSearch')}>
      {dedupedNlpFilterDisplayNames.map((displayName, i) => renderNlpFilter(displayName, i, cssClasses))}
      {removableFiltersWithHandlers.map(({ filter, handleRemove }, i) => {
        return (
          <RemovableFilter
            key={`${filter.displayName ?? 'filter'}-${i}`}
            displayName={filter.displayName}
            handleRemove={handleRemove}
            cssClasses={cssClasses}
          />
        );
      })}
      {removableFilters.length > 0 &&
        <button onClick={handleClickClearAllButton} className={cssClasses.clearAllButton}>
          {t('clearAll')}</button>
      }
    </div>
  );
}

interface DedupedRemovableFilter extends RemovableFilter {
  duplicates?: RemovableFilter[]
}

function getDedupedRemovableFilters(filters: RemovableFilter[]) {
  const dedupedFilters: DedupedRemovableFilter[] = [];
  for (const f of filters) {
    const preexistingDupe = dedupedFilters.find(d => isDuplicateFieldValueFilter(d.filter, f.filter));
    if (!preexistingDupe) {
      dedupedFilters.push(f);
    } else {
      if (!preexistingDupe.duplicates) {
        preexistingDupe.duplicates = [f];
      } else {
        preexistingDupe.duplicates.push(f);
      }
    }
  }
  return dedupedFilters;
}

function RemovableFilter({
  displayName,
  handleRemove,
  cssClasses
}: {
  displayName: string | undefined,
  handleRemove: () => void,
  cssClasses: AppliedFiltersCssClasses
}): React.JSX.Element {
  const { t } = useTranslation();
  return (
    <div className={cssClasses.removableFilter}>
      <div className={cssClasses.filterLabel}>{displayName}</div>
      <button
        className='ml-1 inline-flex h-5 w-5 shrink-0 items-center justify-center text-neutral'
        onClick={handleRemove}
        aria-label={t('removeFilter', { displayName })}
      >
        <CloseIcon className='h-2 w-2' />
      </button>
    </div>
  );
}

function renderNlpFilter(
  displayName: string | undefined,
  index: number,
  cssClasses: AppliedFiltersCssClasses
): React.JSX.Element {
  return (
    <div className={cssClasses.nlpFilter} key={`${displayName}-${index}`}>
      <span className={cssClasses.filterLabel}>{displayName}</span>
    </div>
  );
}
