import clsx from 'clsx'
import React, { FC, useEffect, useState } from 'react'

import { theming } from '../../themes'
import { ValidationOption, ValidationSchema, ValidatorKeysType, validator } from '../../utils/validator'
import { Input, InputProps } from '../input'
import { useStyles } from './styles'

export type ValidatedInputProps = {
  onError?: (invalid: boolean) => void
  schema: ValidationSchema
  errorDelayTime?: number
  value: string
  maxLength?: number | string
  wrapperClass?: string
} & InputProps

export const ValidatedInput: FC<ValidatedInputProps> = ({ onError, onChange, schema, wrapperClass, ...otherProps }) => {
  const { validatedInputWrapper, hidden, errorMessageClass } = useStyles({ theme: theming.useTheme() })

  const errorInit = new Map<ValidatorKeysType, string>()

  const [error, setError] = useState(errorInit)

  useEffect(() => {
    if (onError) onError(Boolean(error))
  }, [error, onError])

  const validate = (value: string) => {
    Object.keys(schema).forEach((item) => {
      const key = item as ValidatorKeysType

      const validFn = validator[key]
      const option = schema[key] as ValidationOption

      if (validFn(value, option)) {
        setError((prev) => prev.set(key, option.errorMessage))
      } else if (error.has(key)) {
        setError((prev) => {
          const updMap = prev
          updMap.delete(key)
          return updMap
        })
      }
    })
  }

  const handleChange = (value: string) => {
    if (!value) {
      setError((prev) => {
        const updMap = prev
        updMap.clear()
        return updMap
      })
    } else {
      validate(value)
    }

    if (onChange) onChange(value)
  }

  const errorMessage = Array.from(error.values()).pop() || ''

  return (
    <div className={clsx(validatedInputWrapper, wrapperClass)}>
      <Input onChange={handleChange} error={!!error.size} {...otherProps} />
      <p className={clsx(errorMessageClass, !error.size && hidden)}>{errorMessage}</p>
    </div>
  )
}
