import { ErrorMessage } from '@hookform/error-message'
import { TextInput, Flex, TextInputProps, Divider } from '@tidbcloud/uikit'
import { useRef, useState, useCallback, RefAttributes, forwardRef } from 'react'
import { Controller, RegisterOptions, useFormContext } from 'react-hook-form'

const KEYCODE = {
  BACK_SPACE: 8
}

interface FormTextInputProps extends TextInputProps {
  name: string
  rules?: RegisterOptions
}

export const FormTextInput: React.FC<React.PropsWithChildren<FormTextInputProps & RefAttributes<HTMLInputElement>>> =
  forwardRef(({ name, rules, onChange, ...rest }, ref) => {
    const {
      control,

      formState,
      getFieldState
    } = useFormContext()
    const { error } = getFieldState(name, formState)

    return (
      <Controller
        control={control}
        name={name}
        rules={rules}
        render={({ field }) => {
          const { onChange: handleChange, ref: onRef, ...restField } = field
          return (
            <TextInput
              onChange={(e) => {
                handleChange(e)
                onChange?.(e)
              }}
              ref={(e) => {
                onRef?.(e)
                if (typeof ref === 'function') {
                  ref?.(e)
                }
              }}
              error={error ? <ErrorMessage errors={error} name={name} /> : undefined}
              {...restField}
              {...rest}
            />
          )
        }}
      />
    )
  })

export const VerificationCodeInput: React.FC<
  React.PropsWithChildren<{ name: string; defaultValue?: string; disabled?: boolean }>
> = ({ name, defaultValue, disabled = true }) => {
  const { setValue: setKeyValue } = useFormContext()
  const [value, setValue] = useState(defaultValue || '')

  const codeLen = 8

  const inputsRef = useRef<Array<HTMLInputElement | null>>([])
  const curFocusIndexRef = useRef(value.length ? value.length - 1 : 0)

  const focusInput = useCallback((i: any) => {
    const inputs = inputsRef.current
    if (i >= inputs.length) return
    const input = inputs[i]
    if (!input) return
    input.focus()
    curFocusIndexRef.current = i
  }, [])

  const focusNextInput = useCallback(() => {
    const curFoncusIndex = curFocusIndexRef.current
    const nextIndex = curFoncusIndex + 1 >= codeLen ? codeLen - 1 : curFoncusIndex + 1
    focusInput(nextIndex)
  }, [focusInput])

  const focusPrevInput = useCallback(() => {
    const curFoncusIndex = curFocusIndexRef.current
    let prevIndex
    if (curFoncusIndex === codeLen - 1 && value.length === codeLen) {
      prevIndex = codeLen - 1
    } else {
      prevIndex = curFoncusIndex - 1 <= 0 ? 0 : curFoncusIndex - 1
    }
    focusInput(prevIndex)
  }, [focusInput, value])

  const handleOnDelete = useCallback(() => {
    const curIndex = curFocusIndexRef.current
    if (curIndex === 0) {
      if (!value) return
      setValue('')
    } else if (curIndex === codeLen - 1 && value.length === codeLen) {
      setValue(value.slice(0, curIndex))
    } else {
      setValue(value.slice(0, value.length - 1))
    }
    focusPrevInput()
  }, [focusPrevInput, value])

  const handleOnKeyDown = useCallback(
    (e: any) => {
      switch (e.keyCode) {
        // delete
        case KEYCODE.BACK_SPACE:
          handleOnDelete()
          break
        default:
          break
      }
    },
    [handleOnDelete]
  )

  const handleClick = useCallback(() => {
    focusInput(curFocusIndexRef.current)
  }, [focusInput])

  const handleChange = useCallback(
    (e: any) => {
      const val = e.target.value || ''
      if (val.length === 1) {
        focusNextInput()
        const newVal = value.slice(0, curFocusIndexRef.current) + val + value.slice(curFocusIndexRef.current + 1)
        setValue(newVal)
      }
    },
    [focusNextInput, setValue]
  )

  const handlePaste = useCallback(
    (e: any) => {
      e.preventDefault()
      const val = e.clipboardData
        .getData('text/plain')
        .slice(0, codeLen + 1)
        .replace(/-/g, '') as string
      const len = val.length
      const index = len === codeLen ? codeLen - 1 : len
      setValue(val)
      val.split('').forEach((val, index) => {
        setKeyValue(`${name}.${index}`, val)
      })
      focusInput(index)
    },
    [focusInput, setKeyValue, name]
  )

  const Inputs: React.ReactNode[] = []

  Array.from({ length: codeLen }).forEach((_, index) => {
    Inputs.push(
      <FormTextInput
        name={`${name}.${index}`}
        w={56}
        h={56}
        styles={(theme) => ({
          wrapper: {
            height: '100%'
          },
          input: {
            padding: 10,
            fontSize: 32,
            fontWeight: 700,
            textAlign: 'center',
            color: theme.colors.carbon[8],
            height: '100%',
            ':disabled': {
              color: theme.colors.carbon[8]
            }
          }
        })}
        key={index}
        ref={(ref) => (inputsRef.current[index] = ref)}
        maxLength={1}
        rules={{ required: 'Required' }}
        autoComplete="false"
        onClick={handleClick}
        onChange={handleChange}
        onKeyDown={handleOnKeyDown}
        onPaste={handlePaste}
        disabled={disabled}
      />
    )

    if (index === 3) {
      Inputs.push(
        <Flex align="center" key={index + 'divider'}>
          <Divider w={18} sx={(theme) => ({ borderTopColor: theme.colors.carbon[7] })} />
        </Flex>
      )
    }
  })

  return <Flex justify="space-between">{Inputs}</Flex>
}
