import { useMemo, useEffect, useState } from 'react'
import { DATE_FORMAT_ISO8601_DATE, formatDate, isDateValid, parseDate } from '@navinc/utils'
import { useField, FieldValidator } from 'formik'
import { DateSegmentComponent } from './date-segment-component'
import { InferComponentProps } from '../types'

const INVALID_VALUE = '0000-00-00'
const FULL_YEAR_LENGTH = 4

const toDateValue = (input: string): string => {
  if (!input) {
    return ''
  }

  const parsed = parseDate(input)

  if (!isDateValid(parsed)) {
    return INVALID_VALUE
  }

  return formatDate(parsed, DATE_FORMAT_ISO8601_DATE)
}

const toDateDisplay = (input: string): string => {
  const parsed = parseDate(input)

  if (!isDateValid(parsed)) {
    return ''
  }

  return formatDate(parsed, DATE_FORMAT_ISO8601_DATE)
}

const resolveValue = (input: string) => {
  if (!input.length || input === '--') {
    return ''
  }

  const [year] = input.split('-')

  return year.length === FULL_YEAR_LENGTH ? toDateValue(input) : INVALID_VALUE
}

const createDateValidator =
  ({ isRequired = false, requiredErrorMessage = 'Required', invalidErrorMessage = 'Please enter a valid date' } = {}) =>
  (date: string): [string] | void => {
    if (isRequired && !date) {
      return [requiredErrorMessage]
    }

    if (date === INVALID_VALUE) {
      return [invalidErrorMessage]
    }
  }

type DateSegmentInputProps = Omit<InferComponentProps<typeof DateSegmentComponent>, 'value'> &
  Parameters<typeof createDateValidator>[0] & {
    name: string
    invalidOnTouched?: boolean
  }
export const DateSegmentInput = ({
  name,
  onBlur,
  onChange,
  invalidOnTouched = true,
  isRequired,
  requiredErrorMessage,
  invalidErrorMessage,
  ...props
}: DateSegmentInputProps) => {
  const memoizedValidateDate = useMemo(
    () => createDateValidator({ isRequired, requiredErrorMessage, invalidErrorMessage }),
    [isRequired, requiredErrorMessage, invalidErrorMessage]
  )
  const [field, meta, { setValue }] = useField({ name, validate: memoizedValidateDate as FieldValidator })
  const [rawValue, setRawValue] = useState(() => toDateDisplay(field.value))

  useEffect(() => {
    const converted = toDateValue(field.value)
    if (converted !== field.value) {
      // Ensure initial value stored in parent form is formatted correctly
      setValue(converted)
    }
  }, [field.value, setValue])

  return (
    <DateSegmentComponent
      value={rawValue}
      onChange={(value) => {
        setRawValue(value)
        setValue(resolveValue(value))
        onChange && onChange(value)
      }}
      onBlur={() => {
        field.onBlur({ target: { name } })
        onBlur && onBlur()
      }}
      errors={!invalidOnTouched || meta.touched ? (meta.error as unknown as string[]) : undefined}
      isInvalid={(!invalidOnTouched || meta.touched) && !!meta.error}
      hasSpaceForErrors
      {...props}
    />
  )
}
