import React, { useEffect, useMemo, useState } from 'react'
import { type Meta, type StoryObj } from '@storybook/react-vite'

import { DocsTemplate } from '../../../.storybook'
import MultiSelectComponent, {
  type MultiSelectDataItemBase,
} from './MultiSelect'
import {
  generateArray,
  generateDummyNestedMultiSelectData,
  multiSelectDisabledList,
  multiSelectOptions,
  useApiMultiSelectStory,
} from './MultiSelectStoryHelpers'
import {
  CheckboxState,
  type DateCheckState,
  getFlattenNestedTreeList,
  type TableColumnConfigProps,
} from '../Form/NestedCheckboxHelper'
import { toast } from '../Toast/Toast'

const meta: Meta<typeof MultiSelectComponent> = {
  title: 'Components/FormComponents/MultiSelect',
  component: MultiSelectComponent,
  parameters: {
    design: {
      type: 'figma',
      url: 'https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=4098-3396&t=9R4mlUY42yEaNlLp-4',
    },
    docs: {
      page: () => (
        <DocsTemplate
          figmaLink='https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=4098-3396&t=9R4mlUY42yEaNlLp-4'
          description={
            <span>
              The <code>MultiSelect</code> component is a user interface element
              that allows users to choose multiple options from a list of items.
              It is commonly used for multi-select forms, filtering options, or
              other scenarios where users need to make multiple selections
              simultaneously. The <code>MultiSelect</code> component facilitates
              efficient and convenient data input, especially when dealing with
              sets of choices.
            </span>
          }
          infoBullets={[
            <span key='first-bullet'>
              Implement the <code>MultiSelect</code> component in forms,
              filtering interfaces, or settings sections where users need to
              select multiple options.
            </span>,
            <span key='second-bullet'>
              The <code>MultiSelect</code> component dynamically adds in a{' '}
              <code>SearchBar</code> when there are at least 10 options in the
              list or when the <code>onSearchChange</code> prop is passed in.
            </span>,
          ]}
        />
      ),
    },
  },
}
export default meta

type Story = StoryObj<typeof MultiSelectComponent>

// Data generation
const nestedMultiSelectData = generateDummyNestedMultiSelectData()
const options = multiSelectOptions
const disabledOptionsList = multiSelectDisabledList

// Define common data item type
type StoryDataItem = MultiSelectDataItemBase & {
  name: string
}

// Story wrapper component for standard MultiSelect usage
const StandardMultiSelect = (args: any) => {
  const [selected, setSelected] = useState<StoryDataItem[]>(
    args.selectedOptions || [],
  )

  return (
    <div style={{ width: 300 }}>
      <MultiSelectComponent
        options={args.options || []}
        selectedOptions={selected}
        labelKey='name'
        selectPlaceholder={args.selectPlaceholder || '-- Select People --'}
        callout={(selectedList: StoryDataItem[]) => {
          setSelected(selectedList)
          args.callout?.(selectedList)
        }}
        formLabelProps={args.formLabelProps}
        disabled={args.disabled}
        loading={args.loading}
        hideSelectAll={args.hideSelectAll}
        isReorderable={args.isReorderable}
        disabledItemsSection={args.disabledItemsSection}
        emptyStateProps={args.emptyStateProps}
        searchBarProps={args.searchBarProps}
        {...args}
      />
    </div>
  )
}

// Story wrapper component for API-driven MultiSelect (pagination)
const PaginatedMultiSelect = (args: any) => {
  const { apiData, selectedData, fetchNew, setSelectedData } =
    useApiMultiSelectStory()

  return (
    <div style={{ width: 300 }}>
      <MultiSelectComponent
        options={apiData}
        selectedOptions={selectedData}
        labelKey='title'
        selectPlaceholder='--- Users ---'
        fetchData={fetchNew}
        hasMore={apiData.length < 100}
        callout={(selectedList: any) => {
          setSelectedData(selectedList)
          args.callout?.(selectedList)
        }}
        searchBarProps={{
          show: false,
        }}
        {...args}
      />
    </div>
  )
}

// Story wrapper component for nested MultiSelect
const NestedMultiSelect = (args: any) => {
  const [selected, setSelected] = useState<any[]>(args.selectedOptions || [])

  // Handle nested functionality
  const flattenData = useMemo(() => {
    if (!args.isNested) return []
    const dataToFlatten = args.options || nestedMultiSelectData
    return getFlattenNestedTreeList({
      nestedData: dataToFlatten,
      childrenKey: 'children',
      parentKey: 'parent_category_id',
      idKey: 'id',
    })
  }, [args.isNested, args.options])

  const defaultItemStates = useMemo(
    () =>
      flattenData.map((i: any) => ({
        id: i['id'] as string | number,
        state: CheckboxState.UNCHECKED,
      })),
    [flattenData],
  )

  const identifierKeys = {
    displayKey: 'name',
    idKey: 'id',
    parentKey: 'parent_category_id',
  }

  const tableConfig: TableColumnConfigProps<any>[] = [
    {
      label: '',
      dataKey: 'name',
      children: (data: any) => (
        <span className='text-xs'>{data.name as string}</span>
      ),
    },
  ]

  const [itemStates, setItemStates] =
    useState<DateCheckState[]>(defaultItemStates)

  // Update itemStates when flattenData changes
  useEffect(() => {
    setItemStates(defaultItemStates)
  }, [defaultItemStates])

  return (
    <div style={{ width: 300 }}>
      <MultiSelectComponent
        options={args.options || nestedMultiSelectData}
        selectedOptions={selected}
        labelKey='name'
        selectPlaceholder={args.selectPlaceholder || '-- Select Category --'}
        callout={(selectedList: any) => {
          setSelected(selectedList)
          args.callout?.(selectedList)
        }}
        nestedConfig={{
          data: flattenData,
          itemStates: itemStates,
          setItemStates: setItemStates,
          identifierKeys: identifierKeys,
          tableConfig,
          ignoreNodeHierarchy: args.ignoreNodeHierarchy,
        }}
        searchBarProps={{
          show: true,
        }}
        formLabelProps={args.formLabelProps}
        emptyStateProps={args.emptyStateProps}
        {...args}
      />
    </div>
  )
}

// Story wrapper for custom disabled key functionality
const CustomDisabledKeyMultiSelect = (args: any) => {
  const [selected, setSelected] = useState<any[]>([])
  const [listOptions, setListOptions] = useState<any[]>(args.options || [])
  const [isMaxItemsSelected, setIsMaxItemsSelected] = useState(false)

  useEffect(() => {
    const baseOptions = args.options || []
    const unselectedItems = baseOptions
      .filter(
        (option: any) =>
          !selected.find(
            (selectedOption: any) => selectedOption.name === option.name,
          ),
      )
      .map((option: any) => ({
        ...option,
        disabled: isMaxItemsSelected,
      }))
    setListOptions(unselectedItems)
  }, [selected, isMaxItemsSelected, args.options])

  return (
    <div style={{ width: 300 }}>
      <MultiSelectComponent
        options={listOptions}
        selectedOptions={selected}
        labelKey='name'
        selectPlaceholder={args.selectPlaceholder || '-- Select People --'}
        callout={(selectedList: any) => {
          setSelected(selectedList)
          if (selectedList.length >= 5) {
            setIsMaxItemsSelected(true)
          } else {
            setIsMaxItemsSelected(false)
          }
          args.callout?.(selectedList)
        }}
        searchBarProps={{
          placeholder: args.searchBarProps?.placeholder,
          show: true,
        }}
        formLabelProps={args.formLabelProps}
        disabledItemsSection={args.disabledItemsSection}
        {...args}
      />
    </div>
  )
}

const StandardMultiSelectWithMinWidth = (args: any) => {
  const [selected, setSelected] = useState<StoryDataItem[]>(
    args.selectedOptions || [],
  )
  return (
    <div style={{ width: 150 }}>
      <MultiSelectComponent
        options={args.options || []}
        selectedOptions={selected}
        labelKey='name'
        selectPlaceholder={args.selectPlaceholder || '-- Select People --'}
        callout={(selectedList: StoryDataItem[]) => {
          setSelected(selectedList)
          args.callout?.(selectedList)
        }}
        formLabelProps={args.formLabelProps}
        disabled={args.disabled}
        loading={args.loading}
        hideSelectAll={args.hideSelectAll}
        isReorderable={args.isReorderable}
        disabledItemsSection={args.disabledItemsSection}
        emptyStateProps={args.emptyStateProps}
        searchBarProps={args.searchBarProps}
        {...args}
      />
    </div>
  )
}
// Basic Stories
export const Basic: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const FewOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: generateArray(3),
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const NoOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: [],
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const Required: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    formLabelProps: {
      label: 'People',
      required: true,
    },
  },
}

export const LabelTooltip: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    formLabelProps: {
      label: 'People',
      tooltip: {
        tooltipContent: 'This is the tooltip content.',
      },
    },
  },
}

export const RightLabel: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    formLabelProps: {
      label: 'People',
      rightLabel: 'Right Label',
    },
  },
}

export const WithoutLabel: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    formLabelProps: undefined,
  },
}

export const SelectedOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [options[8], options[5]],
    formLabelProps: { label: 'People' },
  },
}

export const DisabledOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: disabledOptionsList,
    selectedOptions: [
      disabledOptionsList[22],
      disabledOptionsList[32],
      disabledOptionsList[5],
    ],
    formLabelProps: { label: 'People' },
  },
}

export const ManyOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: generateArray(1000),
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const LongNameOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: [
      {
        ...options[0],
        name: 'This is a long option name that will wrap to the next line and continue with another sentence to demonstrate the wrapping behavior',
      },
      ...options.slice(1),
    ],
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const WordBreak: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: [
      {
        ...options[0],
        name: 'This is a loooooooooooooooooooooooooooooooooooooooooong character name',
      },
      ...options.slice(1),
    ],
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const WithSecondaryOptions: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: generateArray(50, true),
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}

export const Disabled: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    disabled: true,
    formLabelProps: { label: 'People' },
  },
}

export const Loading: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    loading: true,
    formLabelProps: { label: 'People' },
  },
}

export const HideSelectAll: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    hideSelectAll: true,
    formLabelProps: { label: 'People' },
  },
}

export const ReorderableMultiSelect: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    isReorderable: true,
    formLabelProps: { label: 'People' },
  },
}

export const DisabledItemsSection: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: disabledOptionsList,
    selectedOptions: [],
    disabledItemsSection: {
      enabled: true,
    },
    formLabelProps: { label: 'People' },
  },
}

export const DisabledSectionHeaderTitle: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: disabledOptionsList.map((item) =>
      item.disabled ? { ...item, secondaryOption: 'Secondary Text' } : item,
    ),
    selectedOptions: [],
    disabledItemsSection: {
      enabled: true,
      primaryTitle: 'Removed',
      secondaryTitle: 'Reason',
    },
    formLabelProps: { label: 'People' },
  },
}

export const DisabledSectionWithNoDisabledItems: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    disabledItemsSection: {
      enabled: true,
    },
    formLabelProps: { label: 'People' },
  },
}

export const DisabledSectionWithCustomDisabledKey: Story = {
  render: (args) => <CustomDisabledKeyMultiSelect {...args} />,
  args: {
    options: disabledOptionsList.map((item) =>
      item.disabled
        ? {
            ...item,
            secondaryOption: 'Secondary Text',
            notAllowed: true,
          }
        : item,
    ),
    selectedOptions: [],
    formLabelProps: {
      label: 'People',
      rightLabel: 'Max 5 options can be selected',
    },
    disabledItemsSection: {
      enabled: true,
      primaryTitle: 'Removed',
      secondaryTitle: 'Reason',
      disabledItemsKey: 'notAllowed',
    },
  },
}

export const MultiSelectWithPagination: Story = {
  render: (args) => <PaginatedMultiSelect {...args} />,
  args: {},
}

export const Nested: Story = {
  render: (args) => <NestedMultiSelect {...args} isNested={true} />,
  args: {
    options: nestedMultiSelectData,
    selectedOptions: [],
    emptyStateProps: {
      primaryText: 'No Category Found',
      secondaryText: 'Try searching for a different category',
    },
    formLabelProps: {
      label: 'Category',
    },
    selectPlaceholder: '-- Select Category --',
  },
}

export const DisabledHierarchy: Story = {
  render: (args) => (
    <NestedMultiSelect {...args} isNested={true} ignoreNodeHierarchy={true} />
  ),
  args: {
    options: nestedMultiSelectData,
    selectedOptions: [],
  },
  parameters: {
    docs: {
      description: {
        story: `This example demonstrates the MultiSelect with disabled hierarchy behavior in nested mode.
When \`ignoreNodeHierarchy\` is set to \`true\`, selecting or deselecting a node will not affect its parent or child nodes.
This is useful when you want to allow independent selection of nodes regardless of their hierarchical relationship.`,
      },
    },
  },
}

export const OnFocus: Story = {
  render: (args) => <StandardMultiSelect {...args} />,
  args: {
    options: options,
    selectedOptions: [],
    onFocus: () => {
      toast({ type: 'success', message: 'onFocus called' })
    },
  },
}

export const minWidthMultiSelect: Story = {
  render: (args) => <StandardMultiSelectWithMinWidth {...args} />,
  args: {
    options: generateArray(30),
    selectedOptions: [],
    formLabelProps: { label: 'People' },
  },
}
