import { Data } from '../../types/data';

export enum FilterTypes {
  FreeText,
  Numeric,
  Options,
  SearcheableOptions,
  Date,
  DateTime,
  Custom,
  MultipleOptions,
}

export interface FilterConfig<T extends Data> {
  /** Label displayed for the filter */
  label: string;
  /** Property of the data the filter applies to */
  property: keyof T;
  /** Type of the filter */
  type: FilterTypes;
  /** Custom renderer for the selected value */
  selectedValueRenderer?: SelectedValueRenderer;
  /** Callback triggered when the filter is validated */
  onValidate?: FilterValidatorFunction<T>;
}

export interface Option {
  label: string;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  value: any;
}

export interface FreeTextFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.FreeText;
}

export interface NumericTextFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.Numeric;
}

export interface OptionsFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.Options;
  options: Option[];
}

export interface MultipleOptions<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.MultipleOptions;
  options: Option[];
}
export interface SearcheableOptionsFilter<T extends Data>
  extends FilterConfig<T> {
  type: FilterTypes.SearcheableOptions;
  /** Callback triggered when the search input changes. This is also used to resolve the selected option from the value. */
  optionsProvider: (searchText: string, value?: unknown) => Option[];
  /** Placeholder for the search input */
  searchInputPlaceholder?: string;
  /** Maximum number of items to display */
  maxItems?: number;
}

export interface DateFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.Date;
}

export interface DateTimeFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.DateTime;
}

export interface CustomFilter<T extends Data> extends FilterConfig<T> {
  type: FilterTypes.Custom;
  component: React.FC<CustomFilterProps>;
}

export interface CustomFilterProps {
  /** Current Value of the filter */
  value?: FilterValue;

  /** Wether or not the filter is active (default: false) */
  active?: boolean;

  /**
   * Optional id for the label element.
   * Assign to the label's `id` and reference from the input or group via `aria-labelledby`.
   */
  labelId?: string;

  /** Callback triggered when a new filter value is selected */
  onSelect: (
    value: FilterValue,
    /** if set, this value is used when displaying the filter value to the user */
    stringValue?: string,
  ) => void;

  /** Callback triggered when validation fails */
  onError?: () => void;

  /** Callback triggered for custom validations of the filter */
  onValidate?: (currentValue: FilterValue) => FilterValidationResult;
}

export type FilterValue = unknown;

export type FilterType<T extends Data> =
  | FreeTextFilter<T>
  | NumericTextFilter<T>
  | OptionsFilter<T>
  | SearcheableOptionsFilter<T>
  | DateFilter<T>
  | DateTimeFilter<T>
  | CustomFilter<T>
  | MultipleOptions<T>;

export type FilterValues<T> = {
  [K in keyof T]?: FilterValue;
};

export type FilterValidationResult = string | null | undefined;

export type FilterValidatorFunction<T> = (
  value: FilterValue,
  allValues: FilterValues<T>,
) => FilterValidationResult;

export type SelectedValueRenderer = (value: FilterValue) => string;
