import { useEffect, ChangeEvent, FocusEvent, FocusEventHandler, ChangeEventHandler, useState, useMemo } from 'react'
import { Input } from '../input.js'
import { FieldValidator, useField } from 'formik'
import VMasker from 'vanilla-masker'
import { InferComponentProps } from '../types.js'
import { InputWithSensitivityButton } from './input-with-sensitivity-button'

const nonDigitsAndMaskRegExp = /[^\dX]/g
const removeFormatting = (input: string | number) => String(input).replace(nonDigitsAndMaskRegExp, '')

const nonDigitsRegExp = /\D/g
const removeNonDigits = (input: string | number) => String(input).replace(nonDigitsRegExp, '')

export const formatSSN = (input: string | number) => VMasker.toPattern(input, 'SSS-SS-9999')
export const maskSSN = (input: string | number) => {
  const raw = removeFormatting(input)
  return formatSSN(raw.slice(0, 5).replace(/\d/g, 'X') + raw.slice(5))
}

const createSsnValidator =
  ({
    isRequired = false,
    requiredErrorMessage = 'Required',
    lengthErrorMessage = 'We need your full 9-digit social security number',
  } = {}) =>
  (ssnInput: string): [string] | void => {
    if (isRequired && !ssnInput) {
      return [requiredErrorMessage]
    }

    if (ssnInput && removeNonDigits(ssnInput).length < 9) {
      return [lengthErrorMessage]
    }
  }

type SsnInputProps = InferComponentProps<typeof Input> &
  Parameters<typeof createSsnValidator>[0] & {
    name: string
    onBlur?: FocusEventHandler<HTMLInputElement>
    onChange?: ChangeEventHandler<HTMLInputElement>
    invalidOnTouched?: boolean
  }

export const SsnInput = ({
  name,
  onBlur,
  onChange,
  invalidOnTouched = true,
  isRequired,
  requiredErrorMessage,
  lengthErrorMessage,
  ...props
}: SsnInputProps) => {
  const memoizedValidateSSN = useMemo(
    () => createSsnValidator({ isRequired, requiredErrorMessage, lengthErrorMessage }),
    [isRequired, requiredErrorMessage, lengthErrorMessage]
  )
  const [field, meta, { setValue }] = useField({ name, validate: memoizedValidateSSN as FieldValidator })
  const [isVisible, setIsVisible] = useState(false)

  useEffect(() => {
    // Ensure initial value stored in parent form is a number
    const rawSSN = removeNonDigits(field.value)
    if (rawSSN !== field.value) {
      setValue(rawSSN)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return (
    <InputWithSensitivityButton
      type="text"
      inputMode="numeric"
      name={name}
      value={isVisible ? formatSSN(field.value) : maskSSN(field.value)}
      onChange={(e: ChangeEvent<HTMLInputElement>) => {
        const { value } = e.target
        const rawFieldValue = removeNonDigits(field.value)
        const rawValue = removeFormatting(value)

        const newSSN = rawValue.includes('X')
          ? removeNonDigits((rawFieldValue + rawValue.slice(rawFieldValue.length)).slice(0, rawValue.length))
          : rawValue

        const newMaxLengthSSN = newSSN.slice(0, 9)
        setValue(newMaxLengthSSN)
        onChange && onChange(e)
      }}
      onBlur={(e: FocusEvent<HTMLInputElement>) => {
        field.onBlur(e)
        onBlur && onBlur(e)
      }}
      errors={(!invalidOnTouched || meta.touched) && meta.error}
      isInvalid={(!invalidOnTouched || meta.touched) && !!meta.error}
      hasSpaceForErrors
      {...props}
      isVisible={isVisible}
      onToggle={() => setIsVisible((prevIsVisible) => !prevIsVisible)}
      isPrivate
      autocomplete="off"
      onCopy={(e: InputEvent) => {
        if (!isVisible) {
          e.preventDefault()
        }
      }}
    />
  )
}
