import React, { forwardRef } from 'react';
import type {
  TextInputProps as NativeTextInputProps,
  TextInput as RNTextInput,
  StyleProp,
  ViewStyle,
  TextStyle,
} from 'react-native';
import type { State } from './StyledSearch';
import {
  StyledAffixContainer,
  StyledContainer,
  StyledInput,
  StyledInputContainer,
} from './StyledSearch';
import type { IconName } from '../Icon';
import { renderPrefix, renderSuffix } from './utils';
import Button from '../Button';
import { useTheme } from '../../theme';

type SearchOneLineHandles = Pick<
  RNTextInput,
  'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps'
>;

interface SearchOneLineProps extends NativeTextInputProps {
  /**
   * Name of Icon or ReactElement to render on the left side of the input, before the user's cursor.
   */
  prefix?: IconName | React.ReactElement;
  /**
   * Name of Icon or ReactElement to render on the right side of the input.
   */
  suffix?: IconName | React.ReactElement;
  /**
   * Additional wrapper style.
   */
  style?: StyleProp<ViewStyle>;
  /**
   * Input text style.
   */
  textStyle?: StyleProp<TextStyle>;
  /**
   * Testing id of the component.
   */
  testID?: string;
  /**
   * Accessibility label for the input (Android).
   */
  accessibilityLabelledBy?: string;
  /**
   *  Placeholder text to display.
   * */
  placeholder?: string;
  /**
   * Whether the input is editable.
   * */
  editable?: boolean;
  /**
   * Whether the input is disabled.
   */
  disabled?: boolean;
  /**
   * The max length of the input.
   * If the max length is set, the input will display the current length and the max length.
   * */
  maxLength?: number;
  /**
   * Whether to hide the character count.
   * */
  renderInputValue?: (inputProps: NativeTextInputProps) => React.ReactNode;
  /**
   * Variant of the SearchTwo
   * @default 'basic'
   */
  variant?: 'basic' | 'reversed';
  /**
   * Clearable input.
   */
  clearable?: boolean;
  /**
   * If true, indicates that the SearchOneLine is accessible to screen readers.
   */
  accessible?: boolean;
}

export const getState = ({
  disabled,
  editable,
  isEmptyValue,
}: {
  disabled?: boolean;
  editable?: boolean;
  isFocused?: boolean;
  isEmptyValue?: boolean;
}): State => {
  if (disabled) {
    return 'disabled';
  }
  if (!editable) {
    return 'readonly';
  }
  if (!isEmptyValue) {
    return 'filled';
  }
  return 'default';
};

const renderInput = ({
  nativeInputProps,
  ref,
}: {
  nativeInputProps: NativeTextInputProps;
  ref?: React.Ref<RNTextInput>;
}) => {
  return <StyledInput {...nativeInputProps} ref={ref} />;
};

const SearchOneLine = forwardRef<SearchOneLineHandles, SearchOneLineProps>(
  (props, ref) => {
    const {
      prefix = 'search-outlined',
      suffix,
      style,
      allowFontScaling,
      accessibilityLabelledBy,
      editable = true,
      maxLength,
      value,
      defaultValue,
      placeholder,
      disabled = false,
      testID,
      variant = 'basic',
      clearable = false,
      accessible,
      ...nativeProps
    } = props;

    const [isFocused, setIsFocused] = React.useState(false);
    const theme = useTheme();
    const displayText = (value !== undefined ? value : defaultValue) ?? '';
    const isEmptyValue = displayText.length === 0;

    const state = getState({
      disabled,
      editable,
      isFocused,
      isEmptyValue,
    });

    const innerTextInput = React.useRef<RNTextInput | undefined | null>(null);

    React.useImperativeHandle(
      ref,
      () => ({
        // we don't expose this method, it's for testing https://medium.com/developer-rants/how-to-test-useref-without-mocking-useref-699165f4994e
        getNativeTextInputRef: () => innerTextInput.current,
        setNativeProps: (args: NativeTextInputProps) =>
          innerTextInput.current?.setNativeProps(args),
        focus: () => {
          innerTextInput.current?.focus();
        },
        blur: () => innerTextInput.current?.blur(),
        clear: () => innerTextInput.current?.clear(),
        isFocused: () => innerTextInput.current?.isFocused() || false,
      }),
      [innerTextInput]
    );

    const renderClearButton = () => {
      return (
        <StyledAffixContainer>
          <Button.Icon
            onPress={() => {
              innerTextInput.current?.clear();
              nativeProps.onChangeText?.('');
            }}
            intent="text"
            testID="input-clear-button"
            icon="circle-cancel"
            size="xsmall"
            disabled={state === 'disabled' || state === 'readonly'}
          />
        </StyledAffixContainer>
      );
    };

    const shouldShowClearButton =
      clearable && (state === 'filled' || isFocused) && !isEmptyValue;

    return (
      <StyledContainer
        pointerEvents={
          state === 'disabled' || state === 'readonly' ? 'none' : 'auto'
        }
        testID={testID}
        style={style}
        accessible={accessible}
      >
        <StyledInputContainer themeVariant={variant}>
          {renderPrefix({ prefix })}
          {renderInput({
            nativeInputProps: {
              testID: 'text-input',
              accessibilityState: {
                disabled: state === 'disabled' || state === 'readonly',
              },
              accessibilityLabelledBy,
              allowFontScaling,
              ...nativeProps,
              onFocus: (event) => {
                setIsFocused(true);
                nativeProps.onFocus?.(event);
              },
              onBlur: (event) => {
                setIsFocused(false);
                nativeProps.onBlur?.(event);
              },
              editable,
              maxLength,
              value,
              defaultValue,
              placeholder,
              placeholderTextColor:
                theme.__hd__.textInput.colors.placeholder[state],
            },
            ref: (rnTextInputRef) => {
              innerTextInput.current = rnTextInputRef;
            },
          })}
          {shouldShowClearButton
            ? renderClearButton()
            : !!suffix && renderSuffix({ suffix })}
        </StyledInputContainer>
      </StyledContainer>
    );
  }
);

export default SearchOneLine;
