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

export interface SingleSelectProps<
  V,
  T extends SelectOptionType<V> = SelectOptionType<V>
> extends SelectProps<V, T> {
  /**
   * Current selected value.
   */
  value: V | null;
  /**
   * On select event handler
   */
  onConfirm: (value: V | null) => void;
  /**
   * Customise the selected value rendering.
   */
  renderSelectedValue?: (
    selectedValue: V | null,
    inputProps: NativeTextInputProps
  ) => React.ReactNode;
  /**
   * Supported orientations for the Select modal, iOS only.
   */
  supportedOrientations?: ('portrait' | 'landscape')[];
}

const SingleSelect = <V, T extends SelectOptionType<V>>({
  label,
  loading = false,
  inputProps,
  onConfirm,
  onDismiss,
  onEndReached,
  onQueryChange,
  options,
  renderOption,
  renderSelectedValue,
  query,
  error,
  editable = true,
  disabled = false,
  required,
  style,
  testID,
  value,
  supportedOrientations = ['portrait'],
  bottomSheetConfig = {},
}: SingleSelectProps<V, T>) => {
  const { isKeyboardVisible, keyboardHeight } = useKeyboard();
  const [open, setOpen] = useState(false);
  const sectionListRef = useRef<SectionList<T, SectionType>>(null);
  const sections = toSections(options);
  const flatOptions = toFlatOptions(options);
  const displayedValue = flatOptions.find((opt) =>
    deepCompareValue(opt.value, value)
  )?.text;

  const rawValue = value ? String(value) : undefined;
  const { variant: bottomSheetVariant, header: bottomSheetHeader } =
    bottomSheetConfig;

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

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

    return undefined;
  }, [editable, disabled, open]);

  return (
    <>
      <View
        pointerEvents={
          !editable || disabled || inputProps?.loading ? 'none' : 'auto'
        }
      >
        <TouchableOpacity
          onPress={onPress}
          testID="single-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
                  ? (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,
        }}
        onAnimationEnd={() => {
          if (open === true) {
            const scrollParams = getScrollParams(value, 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={value}
          onPress={(selectedValue) => {
            setOpen(false);
            onConfirm(selectedValue);
          }}
          sectionListRef={sectionListRef}
          onScrollToIndexFailed={(info) => {
            setTimeout(() => {
              const scrollParams = getScrollParams(value, sections);
              if (scrollParams.itemIndex < info.highestMeasuredFrameIndex) {
                sectionListRef.current?.scrollToLocation(scrollParams);
              }
            }, 500);
          }}
        />
      </BottomSheet>
    </>
  );
};

export default SingleSelect;
