import {forwardRef, useEffect, useCallback, useMemo, useState} from 'react';
import {View} from 'react-native';
import * as Flags3x2 from 'country-flag-icons/string/3x2';
import {SvgXml} from 'react-native-svg';
import {
  TextInput,
  TextInputProps,
  TextInputType,
} from '@components/_core/TextInput';
import {getFormattedPhoneNumber} from '@shared/utils/phone';
import {REGEX_SPACE, NOT_INTEGER_REGEX} from '@shared/regexs';
import {ICountry} from '@shared/countries';

import {
  useInputController,
  UseInputControllerProps,
} from '../../hooks/useInputController';
import {useInputRef} from '../../hooks/useInputRef';
import {InputAccessibilities, InputCommonProps} from '../../interfaces';
import {InputBox} from '../InputBox';
import {InputCore} from '../InputCore';
import {CountryCodeSelector, FlagKey} from './components/CountryCodeSelector';
import {styles} from './styles';
import {
  getPhoneMaxLength,
  getPhoneInitialValue,
  getCountryInitialValue,
  getStripDialCodePrefix,
} from './utils';

interface InputPhoneProps
  extends InputCommonProps,
    InputAccessibilities,
    Omit<TextInputProps, 'size'> {
  defaultCountryCode?: FlagKey;
  disableCountrySelector?: boolean;
}

export const InputPhone = forwardRef<TextInputType, InputPhoneProps>(
  (
    {
      value,
      onChangeText,
      label,
      helper,
      isSuccess,
      isError,
      disabled,
      onFocus,
      onBlur,
      style,
      size,
      defaultCountryCode = 'AR',
      disableCountrySelector,
      ...textInputProps
    }: InputPhoneProps,
    ref,
  ) => {
    const initialCountry = useMemo(
      () => getCountryInitialValue(value || defaultCountryCode),
      [defaultCountryCode, value],
    );
    const initialPhone = useMemo(() => getPhoneInitialValue(value), [value]);

    const [countryCode, setCountryCode] = useState<FlagKey>(
      initialCountry?.code as FlagKey,
    );
    const [phoneNumber, setPhoneNumber] = useState<string>(initialPhone);

    const [dialCode, setDialCode] = useState<string>(
      initialCountry?.dialCode || '',
    );

    const formattedPhone = useMemo(
      () => getFormattedPhoneNumber(dialCode, phoneNumber),
      [dialCode, phoneNumber],
    );

    const maxLength = useMemo(
      () =>
        (dialCode ? dialCode.length + 1 : 0) +
        getPhoneMaxLength(countryCode, formattedPhone),
      [countryCode, dialCode, formattedPhone],
    );

    const displayValue = useMemo(() => {
      return dialCode ? `${dialCode} ${formattedPhone}` : formattedPhone;
    }, [dialCode, formattedPhone]);

    const generatedFullValue = useMemo(
      () =>
        dialCode && phoneNumber
          ? `${dialCode}${phoneNumber}`.replace(REGEX_SPACE, '')
          : '',
      [dialCode, phoneNumber],
    );

    const normalizedValueProp = useMemo(
      () => value?.replace(REGEX_SPACE, '') || '',
      [value],
    );

    useEffect(() => {
      if (normalizedValueProp !== generatedFullValue) {
        if (value) {
          const country = getCountryInitialValue(value);
          const phone = getPhoneInitialValue(value);
          if (country) {
            setCountryCode(country.code as FlagKey);
          }
          setPhoneNumber(phone);
        } else if (normalizedValueProp === '') {
          setPhoneNumber('');
        }
      }
    }, [value, normalizedValueProp, generatedFullValue]);

    const onPhoneNumberChange = useCallback(
      (text: string) => {
        const afterDial = getStripDialCodePrefix(text, dialCode);
        const digitsOnly = afterDial.replace(NOT_INTEGER_REGEX, '');
        setPhoneNumber(prevPhoneNumber =>
          digitsOnly === prevPhoneNumber ? prevPhoneNumber : digitsOnly,
        );
        const newFullValue =
          dialCode && digitsOnly
            ? `${dialCode}${digitsOnly}`.replace(REGEX_SPACE, '')
            : '';
        onChangeText?.(newFullValue);
      },
      [dialCode, onChangeText],
    );

    const onCountryCodeChange = (country: ICountry) => {
      setCountryCode(country.code as FlagKey);
      setDialCode(country.dialCode);
      setPhoneNumber('');
      onChangeText?.('');
    };

    const {inputRef, focused, focusInput, onInputBlur, onInputFocus} =
      useInputRef({ref, onFocus, onBlur});

    return (
      <InputCore
        label={label}
        helper={helper}
        isSuccess={isSuccess}
        isError={isError}
        focused={focused}
        disabled={disabled}
        style={style}>
        <View style={styles.inputPhoneContainer}>
          {!disableCountrySelector && (
            <CountryCodeSelector
              focused={focused}
              isSuccess={isSuccess}
              isError={isError}
              disabled={disabled}
              countryCode={countryCode}
              setCountry={onCountryCodeChange}
            />
          )}
          <InputBox
            isSuccess={isSuccess}
            isError={isError}
            disabled={disabled}
            size={size}
            focused={focused}
            onPress={focusInput}
            style={styles.inputPhone}>
            <>
              {disableCountrySelector && countryCode in Flags3x2 && (
                <SvgXml
                  xml={Flags3x2[countryCode as keyof typeof Flags3x2]}
                  style={styles.inlineFlag}
                />
              )}
              <TextInput
                ref={inputRef}
                value={displayValue}
                onChangeText={onPhoneNumberChange}
                onFocus={onInputFocus}
                onBlur={onInputBlur}
                editable={!disabled}
                keyboardType="phone-pad"
                inputMode="numeric"
                style={styles.inputText}
                maxLength={maxLength}
                {...textInputProps}
              />
            </>
          </InputBox>
        </View>
      </InputCore>
    );
  },
);

export type InputPhoneControllerProps = InputPhoneProps &
  UseInputControllerProps;

export const InputPhoneController = forwardRef<
  TextInputType,
  InputPhoneControllerProps
>(({name, showSuccess, helper, ...props}, ref) => {
  const {onChange, ...inputController} = useInputController({
    name,
    showSuccess,
    helperText: helper,
  });

  return (
    <InputPhone
      {...props}
      {...inputController}
      ref={ref}
      onChangeText={onChange}
    />
  );
});

export type {FlagKey};
