import { faker } from '@faker-js/faker';
import { action } from '@storybook/addon-actions';
import { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';
import { generateItemArray } from '../../helpers/storybook';
import { TextButton } from '../Buttons/TextButton/TextButton';
import { Checkbox } from '../FormElements';
import { Filters } from './Filters';
import {
  CustomFilterProps,
  FilterType,
  FilterTypes,
  FilterValues,
  Option,
} from './Filters.model';
import { createDateRangeFilterValidators } from './Validators/DateRangeFilterValidator';

const meta: Meta<typeof Filters> = {
  title: 'Other Components/Filters',
  component: Filters,
  render: (args) =>
    React.createElement(() => {
      const [filters, setFilters] = useState(args.defaultValues);

      return (
        <Filters
          {...args}
          onFiltersChange={(filters) => {
            setFilters(filters);
            action('onFiltersChange')(filters);
          }}
          defaultValues={filters}
        />
      );
    }),
};
export default meta;

interface FilterStoryData {
  title: string;
  serviceType: string;
  date: Date;
  datetime: Date;
  id: number;
  holderType: string;
  roleTypes: string;
}

const options: Option[] = generateItemArray(10, (index) => ({
  label: faker.lorem.words(),
  value: index,
}));

const searcheableOptions: Option[] = generateItemArray(10, (index) => ({
  label: faker.lorem.words(),
  value: index,
}));

const multipleOptions: Option[] = generateItemArray(10, (index) => ({
  label: faker.lorem.words(),
  value: index,
}));

const textFilter: FilterType<FilterStoryData> = {
  label: 'Title',
  property: 'title',
  type: FilterTypes.FreeText,
};

const optionFilter: FilterType<FilterStoryData> = {
  label: 'Service Type',
  property: 'serviceType',
  type: FilterTypes.Options,
  options: options,
};

const searcheableOptionFilter: FilterType<FilterStoryData> = {
  label: 'Holder Type',
  property: 'holderType',
  type: FilterTypes.SearcheableOptions,
  optionsProvider: (searchText) =>
    searcheableOptions.filter((option) => option.label.includes(searchText)),
};

const multipleOptionFilter: FilterType<FilterStoryData> = {
  label: 'Role Types',
  property: 'roleTypes',
  type: FilterTypes.MultipleOptions,
  options: multipleOptions,
};

const numberFilter: FilterType<FilterStoryData> = {
  label: 'ID',
  property: 'id',
  type: FilterTypes.Numeric,
};

const dateFilter: FilterType<FilterStoryData> = {
  label: 'Date Only',
  property: 'date',
  type: FilterTypes.Date,
};

const dateTimeFilter: FilterType<FilterStoryData> = {
  label: 'Date Time',
  property: 'datetime',
  type: FilterTypes.DateTime,
};

const CustomFilterComponent: React.FC<CustomFilterProps> = ({
  onSelect,
  value,
}) => {
  const options: Record<string, string> = {
    // value: label
    CREW1: 'CREW 1',
    CREW2: 'CREW 2',
    CREW3: 'CREW 3',
  };

  const [state, setState] = useState((value as string[]) ?? []);

  return (
    <div style={{ padding: '10px' }}>
      {Object.keys(options).map((o) => (
        <Checkbox
          key={o}
          name={o}
          value={state.includes(o)}
          label={options[o]}
          onChange={(value) => {
            if (value) {
              setState([...state, o]);
            } else {
              setState([...state].filter((i) => i !== o));
            }
          }}
        />
      ))}
      <div style={{ display: 'grid' }}>
        <TextButton
          text="Apply"
          onButtonClicked={() =>
            onSelect(state, state.map((o) => options[o]).join(', '))
          }
        />
      </div>
    </div>
  );
};

const customFilter: FilterType<{ custom: string }> = {
  label: 'Custom Filter (multi select)',
  property: 'custom',
  type: FilterTypes.Custom,
  selectedValueRenderer: (value: unknown) => {
    const items = value as string[];

    return String(
      `${items.length} item${items.length === 1 ? '' : 's'}: ${items
        .join(', ')
        .replace(/([a-zA-Z])(\d)/g, '$1 $2')}`,
    );
  },
  component: CustomFilterComponent,
};

export const freeTextFilter: StoryObj<typeof Filters> = {
  args: {
    options: [textFilter],
  },
};

export const numericFilter: StoryObj<typeof Filters> = {
  args: {
    options: [numberFilter],
  },
};

export const optionsFilter: StoryObj<typeof Filters> = {
  args: {
    options: [optionFilter],
  },
};

export const searchableOptionsFilter: StoryObj<typeof Filters> = {
  args: {
    options: [searcheableOptionFilter],
  },
};

export const multiOptionFilter: StoryObj<typeof Filters> = {
  args: {
    options: [multipleOptionFilter],
  },
};

export const DateFilter: StoryObj<typeof Filters> = {
  args: {
    options: [dateFilter, dateTimeFilter],
  },
};

export const CustomFilter: StoryObj<typeof Filters> = {
  args: {
    options: [customFilter],
  },
};

export const MultipleFilters: StoryObj<typeof Filters> = {
  args: {},
  render: (args) =>
    React.createElement(() => {
      const [filters, setFilters] = useState<FilterValues<unknown>>();

      return (
        <Filters<FilterStoryData & { custom: string }>
          {...args}
          options={[
            numberFilter,
            textFilter,
            optionFilter,
            dateFilter,
            dateTimeFilter,
            customFilter,
            searcheableOptionFilter,
            multipleOptionFilter,
          ]}
          defaultValues={filters}
          onFiltersChange={(filters) => {
            setFilters(filters);
            action('Filter changed')(filters);
          }}
        />
      );
    }),
};

export const DateRangeFilters: StoryObj<typeof Filters> = {
  render: () =>
    React.createElement(() => {
      const [filters, setFilters] = useState<FilterValues<FilterStoryData>>();

      const [createFromDateFilterValidator, createToDateFilterValidator] =
        createDateRangeFilterValidators<FilterStoryData>();

      return (
        <Filters<FilterStoryData>
          options={[
            {
              label: 'Date From',
              property: 'date',
              type: FilterTypes.Date,
              onValidate: createFromDateFilterValidator('date'),
            },
            {
              label: 'Date To',
              property: 'date',
              type: FilterTypes.Date,
              onValidate: createToDateFilterValidator('date'),
            },
          ]}
          defaultValues={filters}
          onFiltersChange={(filters) => {
            setFilters(filters);
            action('Filter changed')(filters);
          }}
        />
      );
    }),
};
