import { createDebounce } from '@/utils/debounce'
import { isEventWithTarget } from '@/utils/vue-tsx-utils'
import { CaminoDate, caminoDateValidator } from 'camino-common/src/date'
import { isNotNullNorUndefined } from 'camino-common/src/typescript-tools'
import { defineComponent, ref } from 'vue'

type TextInputType = {
  type: 'text' | 'email'
}

type DateInputType = {
  type: 'date'
}

type NumberInputType = {
  type: 'number'
  min?: number
  max?: number
}
type BaseProps = {
  id: string
  legend: { main: string; visible?: boolean; description?: string; placeholder?: string; info?: { type: 'info' | 'error'; value: string } }
  disabled?: boolean
  required: boolean
}

type Props = BaseProps & (TextProps | NumberProps | DateProps)

type TextProps = {
  type: TextInputType
  valueChanged: (value: string) => void
  initialValue?: string | null
}

type NumberProps = {
  type: NumberInputType
  valueChanged: (value: number | null) => void
  initialValue?: number | null
}

type DateProps = {
  type: DateInputType
  valueChanged: (value: CaminoDate | null) => void
  initialValue?: CaminoDate | null
}

const isTextProps = (props: Props): props is BaseProps & TextProps => props.type.type === 'text' || props.type.type === 'email'
const isNumberProps = (props: Props): props is BaseProps & NumberProps => props.type.type === 'number'
const isDateProps = (props: Props): props is BaseProps & DateProps => props.type.type === 'date'

export const DsfrInput = defineComponent<Props>(props => {
  const debounce = createDebounce()

  const value = ref(props.initialValue)
  const updateFromEvent = (e: Event) => {
    if (isEventWithTarget(e)) {
      if (isTextProps(props)) {
        value.value = e.target.value
        props.valueChanged(e.target.value)
      } else if (isNumberProps(props)) {
        const valueAsNumber = e.target.valueAsNumber
        value.value = valueAsNumber
        props.valueChanged(isNaN(valueAsNumber) ? null : valueAsNumber)
      } else if (isDateProps(props)) {
        const dateParsed = caminoDateValidator.safeParse(e.target.value)
        const newValue = dateParsed.success ? dateParsed.data : null
        value.value = newValue
        debounce(() => props.valueChanged(newValue))
      }
    }
  }

  return () => (
    <div
      class={[
        'fr-input-group',
        isNotNullNorUndefined(props.disabled) && props.disabled ? 'fr-input-group--disabled' : null,
        isNotNullNorUndefined(props.legend.info) && props.legend.info.type === 'error' ? 'fr-input-group--error' : null,
      ]}
      style={{ marginBottom: 0 }}
    >
      {(props.legend.visible ?? true) ? (
        <label class="fr-label" for={props.id}>
          {props.legend.main}
          {!props.required ? ' (optionnel)' : null}
          {props.type.type === 'date' ? <span class="fr-hint-text">au format jj/mm/aaaa</span> : null}
          {isNotNullNorUndefined(props.legend.description) ? <span class="fr-hint-text" v-html={props.legend.description}></span> : null}
        </label>
      ) : null}
      <input
        onInput={updateFromEvent}
        placeholder={props.legend.placeholder}
        value={value.value}
        class={['fr-input', isNotNullNorUndefined(props.legend.info) && props.legend.info.type === 'error' ? 'fr-input--error' : null]}
        name={props.id}
        id={props.id}
        disabled={props.disabled ?? false}
        required={props.required}
        {...(props.type ?? { type: 'text' })}
        aria-describedby={isNotNullNorUndefined(props.legend.info) && props.legend.info.value !== '' ? `${props.id}-info` : undefined}
      />
      {isNotNullNorUndefined(props.legend.info) && props.legend.info.value !== '' ? (
        <p id={`${props.id}-info`} class={`fr-${props.legend.info.type}-text`}>
          {props.legend.info.value}
        </p>
      ) : null}
    </div>
  )
})

// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
DsfrInput.props = ['id', 'initialValue', 'valueChanged', 'legend', 'disabled', 'required', 'type']
