<script setup lang="ts">
import { computed, ref, useAttrs, watch } from 'vue'
import { useFdsI18n } from '../../../plugin/useFdsI18n'
import FdsInput from '../FdsInput/FdsInput.vue'
import { getSsnMask, getSsnMaskOptions } from './ssnMask'
import type { FdsSsnEmits, FdsSsnProps } from './types'
import { getSsnValidationState, stripSsnSeparators, type SsnValidationOptions } from '../../../helpers/validateSsn'

defineOptions({
  inheritAttrs: false,
})

const modelValue = defineModel<string>({ default: '' })
const attrs = useAttrs()

const props = withDefaults(defineProps<FdsSsnProps>(), {
  label: undefined,
  meta: undefined,
  optional: false,
  valid: undefined,
  invalidMessage: undefined,
  id: undefined,
  autocomplete: 'off',
  required: false,
  placeholder: undefined,
  name: undefined,
  autofocus: false,
  readonly: false,
  allowCoordinationNumber: true,
  allowInterimNumber: true,
  disabled: false,
  dataTestid: undefined,
  inputClass: undefined,
})

const emit = defineEmits<FdsSsnEmits>()

const { t } = useFdsI18n()

const inputAttrs = computed(() => {
  const { class: _class, style, ...rest } = attrs
  return {
    ...rest,
    ...(style == null ? {} : { style: style as string | Record<string, unknown> }),
  }
})

const forwardedInputProps = computed(() => ({
  id: props.id,
  autocomplete: props.autocomplete,
  required: props.required,
  placeholder: props.placeholder,
  name: props.name,
  autofocus: props.autofocus,
  readonly: props.readonly,
  inputmode: props.allowInterimNumber ? 'text' : 'numeric',
  ...inputAttrs.value,
}))

const validationOptions = computed(
  (): SsnValidationOptions => ({
    allowCoordinationNumber: props.allowCoordinationNumber,
    allowInterimNumber: props.allowInterimNumber,
  }),
)

const ssnMask = computed(() => getSsnMask(props.allowInterimNumber))
const ssnMaskOptions = computed(() => getSsnMaskOptions(props.allowInterimNumber))

/** Set after blur; validation does not run on input. */
const committedValid = ref<boolean | null>(null)

const resolvedLabel = computed(() => (props.label === undefined ? t('FdsSsn.label') : props.label))

const resolvedInvalidMessage = computed(() =>
  props.invalidMessage === undefined ? t('FdsSsn.invalidSsn') : props.invalidMessage,
)

const displayValid = computed((): boolean | null | undefined => {
  if (props.valid === false) return false
  if (props.valid === true) return true
  if (committedValid.value === false) return false
  if (committedValid.value === true) return true
  return props.valid
})

function normalizeValue() {
  const normalized = stripSsnSeparators(modelValue.value ?? '')
  if (normalized !== (modelValue.value ?? '')) {
    modelValue.value = normalized
  }
}

function runValidation() {
  const validationState = getSsnValidationState(modelValue.value ?? '', validationOptions.value)
  committedValid.value = validationState
  emit('valid', validationState)
}

watch([() => props.allowCoordinationNumber, () => props.allowInterimNumber], () => {
  if (committedValid.value === null) {
    return
  }
  runValidation()
})

function handleBlur(ev: FocusEvent) {
  normalizeValue()
  runValidation()
  emit('blur', ev)
}
</script>

<template>
  <div class="w-full">
    <FdsInput
      :model-value="modelValue"
      @update:model-value="modelValue = $event ?? ''"
      :mask="ssnMask"
      :mask-options="ssnMaskOptions"
      :label="resolvedLabel"
      :meta="meta"
      :valid="displayValid"
      :disabled="disabled"
      :optional="optional"
      :invalid-message="resolvedInvalidMessage"
      :ariaLabel="resolvedLabel"
      :data-testid="dataTestid"
      :class="inputClass"
      v-bind="forwardedInputProps"
      @blur="handleBlur"
    />
  </div>
</template>
