import React from 'react';
import {
  dateRangeFilter,
  idNameArrayFilter,
  numberRangeFilter,
  stringOperatorFilter,
} from '@wix/bex-core';
import type {
  Field,
  Filter,
  FilterStateBaseParams,
  FilterOperator,
  PatternsFieldType,
} from '@wix/bex-core';
import type { ToolbarCollectionState } from './ToolbarCollectionState';
import type { FilterElement } from '../components/CollectionToolbarFilters';
import { toFieldType } from '../utils/fieldTypePrefixIcons';
import { CheckboxCustomFieldFilter } from '../components/CustomFieldFilters/CheckboxCustomFieldFilter';
import { DateCustomFieldFilter } from '../components/CustomFieldFilters/DateCustomFieldFilter';
import { DateTimeCustomFieldFilter } from '../components/CustomFieldFilters/DateTimeCustomFieldFilter';
import { OperatorFilterCustomFieldFilter } from '../components/CustomFieldFilters/OperatorFilterCustomFieldFilter';
import { NumberRangeFilter } from '../components/NumberRangeFilter';

type FilterFactory = (
  params: Partial<FilterStateBaseParams<any>>,
) => Filter<any>;

/**
 * The filter behavior a source supplies that the shared builder can't derive
 * from the neutral field type. The neutral type collapses distinct source types
 * (e.g. a DataExtension dropdown into SHORT_TEXT), so the source fills the gaps.
 */
export interface FieldFilterConfig {
  /** Operators the source's backend can run on text fields. */
  textOperators: FilterOperator[];
  /** Whether a NUMBER field's filter accepts decimals. Default: false. */
  numberIsDecimal?: (field: Field) => boolean;
  /** Filter type icon. Default: derived from the neutral field type. */
  iconType?: (field: Field) => ReturnType<typeof toFieldType>;
}

// Field type → the filter element rendered in the toolbar. Its keys also define
// which field types get an auto filter (see isFilterableFieldType).
const FILTER_COMPONENT_BY_TYPE: Partial<
  Record<PatternsFieldType, React.ComponentType<any>>
> = {
  SHORT_TEXT: OperatorFilterCustomFieldFilter,
  LONG_TEXT: OperatorFilterCustomFieldFilter,
  URL: OperatorFilterCustomFieldFilter,
  NUMBER: NumberRangeFilter,
  BOOLEAN: CheckboxCustomFieldFilter,
  DATE: DateCustomFieldFilter,
  DATETIME: DateTimeCustomFieldFilter,
};

// Field type → the filter state registered on the collection query. Text fields
// use the source's allowed operators; the rest are fixed.
const filterFactoryByType = (
  config: FieldFilterConfig,
): Partial<Record<PatternsFieldType, FilterFactory>> => {
  const stringFilter: FilterFactory = (params) =>
    stringOperatorFilter({ ...params, allowedOperators: config.textOperators });
  return {
    SHORT_TEXT: stringFilter,
    LONG_TEXT: stringFilter,
    URL: stringFilter,
    NUMBER: numberRangeFilter,
    BOOLEAN: idNameArrayFilter,
    DATE: dateRangeFilter,
    DATETIME: dateRangeFilter,
  };
};

/** A field gets an auto filter when its type maps to a filter element. */
export const isFilterableFieldType = (type: PatternsFieldType): boolean =>
  type in FILTER_COMPONENT_BY_TYPE;

/**
 * Registers a filter state per filterable field on the collection query, so the
 * built elements have a filter to read. Idempotent — addFilterIfNeeded skips a
 * field that is already registered.
 */
export const registerFieldFilters = (
  toolbar: ToolbarCollectionState<any, any>,
  fields: Field[],
  config: FieldFilterConfig,
): void => {
  const factoryByType = filterFactoryByType(config);
  fields.forEach((field) => {
    const factory = factoryByType[field.type];
    if (!factory) {
      return;
    }
    toolbar.collection.query.addFilterIfNeeded(
      field.id as any,
      factory({ name: field.id, isCustomField: true }),
    );
  });
};

/** Builds the toolbar filter element for a field, or null when it has none. */
const buildFieldFilterElement = (
  toolbar: ToolbarCollectionState<any, any>,
  field: Field,
  config: FieldFilterConfig,
): FilterElement | null => {
  const FilterComponent = FILTER_COMPONENT_BY_TYPE[field.type];
  const filter = toolbar.collection.filters[field.id];
  if (!FilterComponent || !filter) {
    return null;
  }
  const label = field.displayName || field.id;
  const decimal =
    field.type === 'NUMBER' ? config.numberIsDecimal?.(field) : false;
  return (
    <FilterComponent
      key={field.id}
      filter={filter}
      fieldType={config.iconType?.(field) ?? toFieldType(field.type)}
      accordionItemProps={{ label }}
      toolbarItemProps={{ label }}
      toolbarTagProps={{ label }}
      {...(decimal ? { decimal: true } : {})}
    />
  ) as FilterElement;
};

/** Builds the toolbar filter elements for a source's filterable fields. */
export const buildFieldFilters = (
  toolbar: ToolbarCollectionState<any, any>,
  fields: Field[],
  config: FieldFilterConfig,
): FilterElement[] =>
  fields
    .map((field) => buildFieldFilterElement(toolbar, field, config))
    .filter((el): el is FilterElement => el !== null);
