import React, { useCallback, useEffect, useRef, useState } from 'react';
import type {
  TextInputProps as NativeTextInputProps,
  SectionList,
} from 'react-native';
import { TouchableOpacity, View } from 'react-native';
import { useKeyboard } from '../../../utils/helpers';
import BottomSheet from '../../BottomSheet';
import Box from '../../Box';
import type { TextInputProps } from '../../TextInput';
import TextInput from '../../TextInput';
import Footer from '../Footer';
import { getScrollParams, toFlatOptions, toSections } from '../helpers';
import { StyledSearchBar } from '../StyledSelect';
import type { SectionType, SelectOptionType, SelectProps } from '../types';
import OptionList from './OptionList';
import { isOptionSelected } from './utils';

export interface MultiSelectProps<
  V,
  T extends SelectOptionType<V> = SelectOptionType<V>
> extends SelectProps<V, T> {
  /**
   * Current selected value.
   */
  value: V[];
  /**
   * event handler for footer button.
   */
  onConfirm: (value: V[]) => void;
  /**
   * Footer label.
   */
  footerLabel:
    | string
    | (({
        value,
        closeModal,
      }: {
        value: V[];
        closeModal: () => void;
      }) => React.ReactNode);
  /**
   * Customise the selected value rendering.
   */
  renderSelectedValue?: (
    selectedValue: V[],
    inputProps: NativeTextInputProps
  ) => React.ReactNode;
  /**
   * Supported orientations for the MultiSelect modal, iOS only.
   */
  supportedOrientations?: ('portrait' | 'landscape')[];
}

function MultiSelect<V, T extends SelectOptionType<V>>({
  footerLabel,
  label,
  loading = false,
  inputProps,
  onConfirm,
  onDismiss,
  onEndReached,
  onQueryChange,
  options,
  renderOption,
  renderSelectedValue,
  query,
  error,
  editable = true,
  disabled = false,
  required,
  style,
  testID,
  value,
  supportedOrientations = ['portrait'],
  bottomSheetConfig = {},
}: MultiSelectProps<V, T>) {
  const { isKeyboardVisible, keyboardHeight } = useKeyboard();
  const [open, setOpen] = useState(false);
  const [selectingValue, setSelectingValue] = useState(value);
  const sectionListRef = useRef<SectionList<T, SectionType>>(null);
  const sections = toSections(options);
  const flatOptions = toFlatOptions(options);
  const displayedValue = flatOptions
    .filter((opt) => isOptionSelected(value, opt))
    .map((opt) => opt.text)
    .join(', ');
  const rawValue = value.length > 0 ? value.join(', ') : '';
  const { variant: bottomSheetVariant, header: bottomSheetHeader } =
    bottomSheetConfig;

  const onPress = useCallback(() => {
    setOpen(true);
  }, []);

  let selectSuffix: TextInputProps['suffix'];
  if (editable && !disabled) {
    selectSuffix = open ? 'arrow-up' : 'arrow-down';
  }

  useEffect(() => {
    setSelectingValue(value);
  }, [open, value]);
  return (
    <>
      <View
        pointerEvents={
          !editable || disabled || inputProps?.loading ? 'none' : 'auto'
        }
      >
        <TouchableOpacity
          onPress={onPress}
          testID="multi-select-touchable-opacity"
        >
          {
            // prevent users from focusing TextInput
          }
          <View pointerEvents="none">
            <TextInput
              {...inputProps}
              label={label}
              value={renderSelectedValue ? rawValue : displayedValue}
              suffix={selectSuffix}
              multiline
              error={error}
              editable={editable}
              disabled={disabled}
              required={required}
              pointerEvents="none"
              style={style}
              testID={testID}
              renderInputValue={
                renderSelectedValue !== undefined && rawValue?.length > 0
                  ? (nativeInputProps) =>
                      renderSelectedValue(value, nativeInputProps)
                  : undefined
              }
            />
          </View>
        </TouchableOpacity>
      </View>
      <BottomSheet
        variant={bottomSheetVariant || 'fixed'}
        open={open}
        onRequestClose={() => {
          onDismiss?.();
          setOpen(false);
        }}
        header={bottomSheetHeader || label}
        style={{
          paddingBottom: isKeyboardVisible ? keyboardHeight : 0,
        }}
        footer={
          typeof footerLabel === 'string' ? (
            <Footer
              label={footerLabel}
              onPress={() => {
                setOpen(false);
                onConfirm(selectingValue);
              }}
            />
          ) : (
            <Box flex={1} flexDirection="row" justifyContent="flex-end">
              {footerLabel({
                value: selectingValue,
                closeModal: () => setOpen(false),
              })}
            </Box>
          )
        }
        onAnimationEnd={() => {
          if (open === true) {
            const scrollParams = getScrollParams(value?.[0], sections);
            sectionListRef.current?.scrollToLocation(scrollParams);
          }
        }}
        supportedOrientations={supportedOrientations}
      >
        {onQueryChange && (
          <StyledSearchBar>
            <TextInput
              editable
              placeholder="Search"
              suffix="search-outlined"
              onChangeText={onQueryChange}
              value={query}
            />
          </StyledSearchBar>
        )}
        <OptionList
          onQueryChange={onQueryChange}
          onEndReached={onEndReached}
          loading={loading}
          sections={sections}
          renderOption={renderOption}
          value={selectingValue}
          onPress={setSelectingValue}
          sectionListRef={sectionListRef}
          onScrollToIndexFailed={(info) => {
            setTimeout(() => {
              const scrollParams = getScrollParams(value?.[0], sections);

              if (scrollParams.itemIndex < info.highestMeasuredFrameIndex) {
                sectionListRef.current?.scrollToLocation(scrollParams);
              }
            }, 500);
          }}
        />
      </BottomSheet>
    </>
  );
}

export default MultiSelect;
