<template>
  <div class="form-control">
    <label v-if="label" class="label" :for="inputId">
      <span class="label-text">{{ label }}</span>
      <span v-if="required" class="label-text-alt text-error">*</span>
    </label>
    <input
      :id="inputId"
      :value="modelValue"
      type="email"
      class="input input-bordered"
      :class="{ 'input-error': Boolean(errorMessage) }"
      :placeholder="placeholder"
      :required="required"
      :disabled="disabled"
      :name="name"
      :autocomplete="autocomplete"
      :pattern="EMAIL_INPUT_PATTERN"
      :aria-invalid="Boolean(errorMessage)"
      :aria-describedby="describedBy"
      @input="onInput"
      @blur="onBlur"
      @focus="(event) => emit('focus', event)"
    />
    <p v-if="help" :id="helpId" class="mt-2 text-xs text-gray-500">
      {{ help }}
    </p>
    <p v-if="errorMessage" :id="errorId" class="mt-2 text-xs text-error">
      {{ errorMessage }}
    </p>
  </div>
</template>

<script setup lang="ts">
import { computed, useId } from 'vue'
import { normalizeEmail } from '~/utils/formatters/email'
import { EMAIL_INPUT_PATTERN, EMAIL_VALIDATION_ERROR, emailSchema } from '~/shared/validation/email'

type Emits = {
  (event: 'update:modelValue', value: string): void
  (event: 'blur', value: FocusEvent): void
  (event: 'focus', value: FocusEvent): void
}

const emit = defineEmits<Emits>()

const props = withDefaults(
  defineProps<{
    modelValue: string
    label?: string
    required?: boolean
    error?: string | null
    help?: string | null
    placeholder?: string
    disabled?: boolean
    name?: string
    id?: string
    autocomplete?: string
    normalizeOnBlur?: boolean
    validateOnBlur?: boolean
  }>(),
  {
    modelValue: '',
    label: 'Email',
    required: false,
    error: null,
    help: null,
    placeholder: 'ex: contact@example.com',
    disabled: false,
    name: undefined,
    id: undefined,
    autocomplete: 'email',
    normalizeOnBlur: false,
    validateOnBlur: false,
  }
)

const fallbackId = useId()
const inputId = computed(() => props.id || fallbackId)
const helpId = computed(() => (props.help ? `${inputId.value}-help` : undefined))
const errorMessage = computed(() => props.error || null)
const errorId = computed(() => (errorMessage.value ? `${inputId.value}-error` : undefined))
const describedBy = computed(() => [helpId.value, errorId.value].filter(Boolean).join(' ') || undefined)

const onInput = (event: Event) => {
  const target = event.target as HTMLInputElement
  emit('update:modelValue', target.value)
}

const onBlur = (event: FocusEvent) => {
  const target = event.target as HTMLInputElement

  if (props.normalizeOnBlur) {
    const normalized = normalizeEmail(target.value)
    if (normalized !== target.value) {
      target.value = normalized
      emit('update:modelValue', normalized)
    }
  }

  if (props.validateOnBlur && !errorMessage.value) {
    const validation = emailSchema.validate(target.value)
    if (!validation.valid) {
      target.setCustomValidity(EMAIL_VALIDATION_ERROR)
    } else {
      target.setCustomValidity('')
    }
  }

  emit('blur', event)
}
</script>
