import React, { useCallback, useEffect, useRef, useState } from 'react'
import moment from 'moment'

import { c } from '../../translations/LibraryTranslationService'
import styles from './_filter.module.scss'
import DatePicker from '../DatePicker/DatePicker'
import { hasValue } from '../../services/HelperServiceTyped'
import SearchBar from '../SearchBar/SearchBar'
import SelectDisplay from '../Selects/SelectDisplay/SelectDisplay'
import {
  type ExposedFilterNameType,
  type ExposedFilterProps,
} from './ExposedFilter.models'
import RangeFilter from '../RangeFilter/RangeFilter'
import MultiSelect, {
  type MultiSelectDataItemBase,
} from '../MultiSelect/MultiSelect'
import { CheckboxState } from '../Form/NestedCheckboxHelper'
import ExposedFilterShell from './ExposedFilterShell'
import ExposedFilterPopover from './ExposedFilterPopover'
import { useTagLayout } from './useTagLayout'
import Modal from '../Modal/Modal'

// Type guard for checking if an option matches the FilterType interface
function isFilterType<
  T extends MultiSelectDataItemBase & ExposedFilterNameType,
>(option: unknown): option is T {
  return (
    typeof option === 'object' &&
    option !== null &&
    'name' in option &&
    typeof (option as Record<string, unknown>).name === 'string'
  )
}

// Type guard for modal props shape
function hasSelectedOption(
  value: unknown,
): value is { selectedOption?: unknown } {
  return (
    typeof value === 'object' && value !== null && 'selectedOption' in value
  )
}

const ExposedFilter = <
  FilterType extends MultiSelectDataItemBase & ExposedFilterNameType,
>(
  filterValues: ExposedFilterProps<FilterType>,
): React.JSX.Element => {
  const {
    labelName,
    type,
    position,
    disableCloseOnScroll,
    onChangeCallout,
    stateName,
    isClearFilter,
    setIsClearFilter,
    disabled,
    disabledTooltip,
    placeholder,
  } = filterValues
  const containerRef = useRef<HTMLDivElement>(null)
  const [selectSearchValue, setSelectSearchValue] = useState('')

  const exposedFilterDisabledProps =
    disabled === true
      ? { disabled: true as const, disabledTooltip }
      : { disabled: false as const }

  // Clear filter when clear all is clicked
  useEffect(() => {
    if (isClearFilter) {
      if (type === 'date') onChangeCallout?.(stateName, undefined)
      if (type === 'search') onChangeCallout?.(stateName, '')
      setIsClearFilter?.(false)
    }
  }, [isClearFilter, setIsClearFilter, onChangeCallout, stateName, type])

  // Precompute flags
  const isMultiSelect = type === 'multi-select'
  const isModal = type === 'modal'

  // Precompute Multi-select derived values for hooks
  const multiSelectProps = isMultiSelect
    ? filterValues.multiSelectProps
    : undefined
  const multiSelectSelectedOptions = multiSelectProps?.selectedOptions
  const multiSelectSelectedLabels = isMultiSelect
    ? (multiSelectSelectedOptions?.map((o) => o.name) ?? [])
    : []
  const {
    selectedTags: multiSelectSelectedTags,
    hasMore: multiSelectHasMore,
    truncateTagIndex: multiSelectTruncateTagIndex,
    truncatedTagWidth: multiSelectTruncatedTagWidth,
  } = useTagLayout(labelName, multiSelectSelectedLabels)

  const multiSelectNestedConfig = multiSelectProps?.nestedConfig
  const multiSelectItemStates = multiSelectNestedConfig?.itemStates
  const multiSelectSetItemStates = multiSelectNestedConfig?.setItemStates

  const resetFilters = useCallback(() => {
    if (!isMultiSelect) return
    if (multiSelectItemStates && multiSelectSetItemStates) {
      const resetItemStates = multiSelectItemStates.map((item) => ({
        ...item,
        state: CheckboxState.UNCHECKED,
      }))
      multiSelectSetItemStates(resetItemStates)
    }
    onChangeCallout?.(stateName, [])
  }, [
    isMultiSelect,
    multiSelectItemStates,
    multiSelectSetItemStates,
    onChangeCallout,
    stateName,
  ])

  // Precompute Modal derived values for hooks
  const modalSelectedOption =
    isModal && hasSelectedOption(filterValues)
      ? filterValues.selectedOption
      : undefined
  const modalSelectedLabels = Array.isArray(modalSelectedOption)
    ? modalSelectedOption.map((v) => String(v))
    : []
  const {
    selectedTags: modalSelectedTags,
    hasMore: modalHasMore,
    truncateTagIndex: modalTruncateTagIndex,
    truncatedTagWidth: modalTruncatedTagWidth,
  } = useTagLayout(labelName, modalSelectedLabels)

  if (type === 'multi-select') {
    const { multiSelectProps } = filterValues
    const multiSelectBaseProps = {
      selectedOptions: multiSelectProps.selectedOptions,
      options: multiSelectProps.options,
      labelKey: multiSelectProps.labelKey,
      callout: (selectedOptions: FilterType[]) =>
        onChangeCallout?.(stateName, selectedOptions),
      formLabelProps: multiSelectProps.formLabelProps,
      loading: multiSelectProps.loading ?? false,
      searchBarProps: {
        ...multiSelectProps.searchBarProps,
        autoFocus: true,
      },
      emptyStateProps: multiSelectProps.emptyStateProps,
      isReorderable: multiSelectProps.isReorderable,
      disabled: multiSelectProps.disabled,
      hideSelectAll: multiSelectProps.hideSelectAll,
      sortAlphabetically: multiSelectProps.sortAlphabetically,
      exposed: true,
      removeBorder: true,
    }

    const multiSelectSharedProps = multiSelectProps.fetchData
      ? {
          ...multiSelectBaseProps,
          fetchData: multiSelectProps.fetchData,
          hasMore: multiSelectProps.hasMore ?? false,
          isFetching: multiSelectProps.isFetching,
          dropdownState: multiSelectProps.dropdownState,
        }
      : multiSelectBaseProps

    return (
      <ExposedFilterPopover
        position={position}
        disableCloseOnScroll={disableCloseOnScroll}
        popoverContent={
          multiSelectProps.nestedConfig ? (
            <MultiSelect
              {...multiSelectSharedProps}
              nestedConfig={multiSelectProps.nestedConfig}
            />
          ) : (
            <MultiSelect {...multiSelectSharedProps} />
          )
        }
      >
        {({ visible, setVisible }) => (
          <ExposedFilterShell
            ref={containerRef}
            label={{ labelName, placeholder, useTrimForLabel: true }}
            {...exposedFilterDisabledProps}
            isActive={(multiSelectProps.selectedOptions?.length ?? 0) > 0}
            tagView={{
              tags: multiSelectSelectedTags,
              totalCount: multiSelectProps.selectedOptions?.length ?? 0,
              hasMore: multiSelectHasMore,
              truncateTagIndex: multiSelectTruncateTagIndex,
              truncatedTagWidth: multiSelectTruncatedTagWidth,
            }}
            onMainClick={() => setVisible(!visible)}
            onClear={
              (multiSelectProps.selectedOptions?.length ?? 0) > 0
                ? resetFilters
                : undefined
            }
          />
        )}
      </ExposedFilterPopover>
    )
  } else if (type === 'date') {
    const { dateProps } = filterValues
    return (
      <ExposedFilterPopover
        position={position}
        disableCloseOnScroll={disableCloseOnScroll}
        maxWidth='max-content'
        popoverContent={
          <DatePicker
            {...dateProps}
            onDateChange={(value) => onChangeCallout?.(stateName, value)}
            showDateValue={true}
            isExposed={true}
          />
        }
      >
        {({ visible, setVisible }) => (
          <ExposedFilterShell
            ref={containerRef}
            label={{ labelName, placeholder }}
            {...exposedFilterDisabledProps}
            isActive={Boolean(
              dateProps.selectedDate || dateProps.selectedDateRange,
            )}
            tagView={{
              tags: dateProps.selectedDate
                ? [moment(dateProps.selectedDate).format('MMM DD, YYYY')]
                : dateProps.selectedDateRange
                  ? [
                      `${moment(dateProps.selectedDateRange?.from).format('MMM DD, YYYY')} - ${moment(dateProps.selectedDateRange.to).format('MMM DD, YYYY')}`,
                    ]
                  : [],
              withMaxWidthOptionContainer: true,
            }}
            onMainClick={() => setVisible(!visible)}
            onClear={
              dateProps.selectedDate || dateProps.selectedDateRange
                ? () => onChangeCallout?.(stateName, undefined)
                : undefined
            }
          />
        )}
      </ExposedFilterPopover>
    )
  } else if (type === 'select') {
    const {
      selectProps: { maxHeight, options, selectedOption, noOptionsMessage },
    } = filterValues

    const filteredOptions = options
      .filter((option) =>
        'name' in option
          ? option.name.toLowerCase().includes(selectSearchValue.toLowerCase())
          : true,
      )
      .map((option) => ({
        ...option,
        label: 'name' in option ? option.name : '',
        name: 'name' in option ? option.name : '',
      }))

    return (
      <ExposedFilterPopover
        position={position}
        disableCloseOnScroll={disableCloseOnScroll}
        popoverContent={({ setVisible }) => (
          <div className={styles.selectContainer}>
            {options?.length > 10 ? (
              <div className='px-2 pt-2'>
                <SearchBar
                  onChange={setSelectSearchValue}
                  value={selectSearchValue}
                />
              </div>
            ) : null}
            {filteredOptions?.length > 0 ? (
              <SelectDisplay
                maxHeight={maxHeight ?? 320}
                options={filteredOptions}
                active={selectedOption?.name}
                callout={(option) => {
                  if (isFilterType<FilterType>(option)) {
                    onChangeCallout?.(stateName, option)
                    setVisible(false)
                  }
                }}
              />
            ) : (
              <div className='p-4 text-xs'>
                {noOptionsMessage ?? c('noResultsFound')}
              </div>
            )}
          </div>
        )}
      >
        {({ visible, setVisible }) => (
          <ExposedFilterShell
            ref={containerRef}
            {...exposedFilterDisabledProps}
            label={{ labelName, placeholder, useTrimForLabel: true }}
            isActive={Boolean(selectedOption)}
            tagView={{
              tags: selectedOption ? [selectedOption.name] : [],
              withMaxWidthOptionContainer: true,
            }}
            onMainClick={() => setVisible(!visible)}
            onClear={
              selectedOption
                ? () => onChangeCallout?.(stateName, undefined)
                : undefined
            }
          />
        )}
      </ExposedFilterPopover>
    )
  } else if (type === 'search') {
    const { searchBarProps } = filterValues
    return (
      <SearchBar
        {...searchBarProps}
        onChange={(value) => onChangeCallout?.(stateName, value)}
      />
    )
  } else if (type === 'range') {
    const { rangeProps } = filterValues
    return (
      <ExposedFilterPopover
        position={position}
        disableCloseOnScroll={disableCloseOnScroll}
        maxWidth='max-content'
        popoverContent={
          <RangeFilter
            selectedValues={rangeProps.selectedValues}
            onChangeCallout={(rangeValues) =>
              onChangeCallout?.(stateName, rangeValues)
            }
          />
        }
      >
        {({ visible, setVisible }) => (
          <ExposedFilterShell
            ref={containerRef}
            label={{ labelName, placeholder }}
            {...exposedFilterDisabledProps}
            isActive={
              hasValue(rangeProps.selectedValues.min) ||
              hasValue(rangeProps.selectedValues.max)
            }
            tagView={{
              tags:
                hasValue(rangeProps.selectedValues.min) &&
                !hasValue(rangeProps.selectedValues.max)
                  ? [`${c('min')}: ${rangeProps.selectedValues.min}`]
                  : hasValue(rangeProps.selectedValues.max) &&
                      !hasValue(rangeProps.selectedValues.min)
                    ? [`${c('max')}: ${rangeProps.selectedValues.max}`]
                    : hasValue(rangeProps.selectedValues.min) &&
                        hasValue(rangeProps.selectedValues.max)
                      ? [
                          `${rangeProps.selectedValues.min} - ${rangeProps.selectedValues.max}`,
                        ]
                      : [],
              withMaxWidthOptionContainer: true,
            }}
            onMainClick={() => setVisible(!visible)}
            onClear={
              hasValue(rangeProps.selectedValues.min) ||
              hasValue(rangeProps.selectedValues.max)
                ? () =>
                    onChangeCallout?.(stateName, {
                      min: undefined,
                      max: undefined,
                    })
                : undefined
            }
          />
        )}
      </ExposedFilterPopover>
    )
  } else if (type === 'modal') {
    const { selectedOption, modalProps } = filterValues

    return (
      <>
        <ExposedFilterShell
          ref={containerRef}
          label={{ labelName, placeholder }}
          {...exposedFilterDisabledProps}
          isActive={
            (Array.isArray(selectedOption) ? selectedOption.length : 0) > 0
          }
          tagView={{
            tags: modalSelectedTags,
            totalCount: Array.isArray(selectedOption)
              ? selectedOption.length
              : 0,
            hasMore: modalHasMore,
            truncateTagIndex: modalTruncateTagIndex,
            truncatedTagWidth: modalTruncatedTagWidth,
          }}
          onMainClick={() => filterValues.openCallout?.()}
          onClear={
            (Array.isArray(selectedOption) ? selectedOption.length : 0) > 0
              ? () => onChangeCallout?.(stateName, undefined)
              : undefined
          }
        />
        <Modal {...modalProps}>{modalProps?.children}</Modal>
      </>
    )
  } else {
    return <></>
  }
}

export default ExposedFilter
