import React, {
  FC,
  forwardRef,
  useContext,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';
import {
  Animated,
  PixelRatio,
  TextInput,
  TextInputFocusEvent,
  TouchableOpacity,
  View,
} from 'react-native';
import { useComponentId } from '../Application';
import { Spacing, Styles } from '../Consts';
import { useScaleSize, Text } from '../Text';
import { ErrorView, getBorderColor } from './common';
import { CaretProps, InputOTPProps } from './index';
import styles from './styles';
import { Icon } from '../Icon';
import SystemTextInput from './SystemTextInput';
import {
  ApplicationContext,
  ComponentContext,
  MiniAppContext,
} from '../Context';

const OTPCaret: FC<CaretProps> = ({ index, length }) => {
  const DURATION = 300;
  const { theme } = useContext(ApplicationContext);
  const opacity = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(opacity, {
          toValue: 1,
          duration: DURATION,
          useNativeDriver: true,
        }),
        Animated.delay(DURATION * 2),
        Animated.timing(opacity, {
          toValue: 0,
          duration: DURATION,
          useNativeDriver: true,
        }),
      ]),
    ).start();
  }, [opacity]);
  const spacingStyle = !isNaN(Number(length)) &&
    index !== Number(length) - 1 && { marginRight: Spacing.L };

  return (
    <View style={[Styles.rowCenter, spacingStyle]}>
      <Animated.View
        style={{
          height: useScaleSize(12),
          width: 1,
          backgroundColor: theme.colors.primary,
          opacity,
        }}
      />
      <Text color={theme.colors.text.hint} typography={'body_default_regular'}>
        -
      </Text>
    </View>
  );
};

const InputOTP = forwardRef(
  (
    {
      length,
      errorMessage,
      errorSpacing,
      floatingValue = 'Label',
      placeholder,
      onChangeText,
      onBlur,
      onFocus,
      dataType = 'number',
      params,
      style,
      hintText,
      ...props
    }: InputOTPProps,
    ref,
  ) => {
    const MAX_LENGTH = 10;
    const [value, setValue] = useState('');
    const [focused, setFocused] = useState(false);
    const inputRef = useRef<TextInput | null>(null);
    const { theme } = useContext(ApplicationContext);
    const context = useContext<any>(MiniAppContext);

    const componentName = 'InputOTP';

    const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;

    const { componentId } = useComponentId(
      `${componentName}/${placeholder}`,
      props.accessibilityLabel,
    );

    useImperativeHandle(ref, () => ({
      onChangeText: (text: string) => {
        _onChangeText(text);
      },
      focus: () => inputRef.current?.focus(),
      blur: () => inputRef.current?.blur(),
      setText: (text: string) => _onChangeText(text),
    }));

    const onFocusInput = (e: TextInputFocusEvent) => {
      setFocused(true);
      onFocus?.(e);
    };

    const onBlurInput = (e: TextInputFocusEvent) => {
      setFocused(false);
      onBlur?.(e);
    };

    const _onChangeText = (text: string) => {
      if (text.length > MAX_LENGTH) return;
      if (dataType === 'number' && isNaN(Number(text))) return;
      if (
        !isNaN(Number(text)) &&
        value.length >= Number(length) &&
        text.length >= value.length
      )
        return;

      setValue(text);
      onChangeText?.(text);
    };

    const onClearText = () => {
      _onChangeText('');
    };

    const fontScale = PixelRatio.getFontScale();

    const renderInputs = (inputLength: number) => {
      const TextInputs: React.ReactNode[] = [];
      for (let i = 0; i < inputLength; i++) {
        TextInputs.push(
          <View key={`otp-input-${i}`}>
            {focused && value.length === i ? (
              <OTPCaret index={i} length={inputLength} />
            ) : (
              <Text
                style={i !== inputLength - 1 && { marginRight: Spacing.L }}
                color={
                  value[i] ? theme.colors.text.default : theme.colors.text.hint
                }
                typography={value[i] ? 'header_m_bold' : 'body_default_regular'}
              >
                {value[i] || '-'}
              </Text>
            )}
          </View>,
        );
      }
      return TextInputs;
    };

    const renderUnidentifiedInputs = () => {
      const splitValue = value.split('');

      if (!focused && value.length === 0 && !!placeholder) {
        return (
          <Text
            color={theme.colors.text.hint}
            typography={'description_default_regular'}
          >
            {placeholder}
          </Text>
        );
      }

      return (
        <View style={Styles.row}>
          {splitValue.map((letter, index) => {
            return (
              <Text
                key={`otp-letter-${index}`}
                style={
                  (index !== value.length - 1 ||
                    (focused && value.length !== MAX_LENGTH)) && {
                    marginRight:
                      fontScale > 1 && value.length > 6 ? Spacing.M : Spacing.L,
                  }
                }
                typography={'header_m_bold'}
              >
                {letter}
              </Text>
            );
          })}
          {focused && value.length !== MAX_LENGTH && <OTPCaret />}
        </View>
      );
    };

    const renderInputView = () => {
      return (
        <TouchableOpacity
          activeOpacity={1}
          onPress={() => inputRef.current?.focus()}
          style={[
            styles.otpInput,
            { backgroundColor: theme.colors.background.surface },
            getBorderColor(theme, focused, errorMessage),
          ]}
        >
          {!!floatingValue && (
            <View
              style={[
                styles.otpFloatingView,
                { backgroundColor: theme.colors.background.surface },
              ]}
            >
              <Text
                typography={'description_default_regular'}
                color={theme.colors.text.hint}
                accessibilityLabel={`${componentId}|floating-text`}
              >
                {floatingValue}
              </Text>
            </View>
          )}
          {value.length > 0 && focused && (
            <TouchableOpacity onPress={onClearText} style={styles.clearIcon}>
              <Icon source={'24_navigation_close_circle_full'} size={12} />
            </TouchableOpacity>
          )}
          <View style={styles.otpInputsView}>
            {length ? renderInputs(length) : renderUnidentifiedInputs()}
          </View>
          <View style={styles.otpRealInputWrapper}>
            <SystemTextInput
              {...props}
              ref={inputRef}
              value={value}
              textAlign={'center'}
              onChangeText={_onChangeText}
              keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
              allowFontScaling={false}
              onFocus={onFocusInput}
              onBlur={onBlurInput}
              style={styles.otpRealInput}
              selectionColor={theme.colors.primary}
              placeholderTextColor={theme.colors.text.hint}
              accessibilityState={{
                disabled: props.disabled,
                ...props.accessibilityState,
              }}
              accessibilityLabel={componentId}
            />
          </View>
        </TouchableOpacity>
      );
    };

    return (
      <ComponentContext.Provider
        value={{
          componentName,
          componentId,
          params,
          state: 'enabled',
        }}
      >
        <View
          style={[
            style,
            styles.otpWrapper,
            showBaseLineDebug && styles.debugBaseLine,
          ]}
        >
          {renderInputView()}
          <ErrorView
            errorMessage={errorMessage}
            errorSpacing={errorSpacing}
            hintText={hintText}
            componentId={componentId}
          />
        </View>
      </ComponentContext.Provider>
    );
  },
);

export default InputOTP;
