<template>
  <field v-bind="$props" :captions="computedCaptions" :errors="computedErrors">
    <RawInputText
      ref="input"
      :type="inputType"
      :modelValue="modelValue"
      @update:model-value="(val) => $emits('update:modelValue', val)"
      :required="required"
      :placeholder="placeholder"
      :disabled="disabled"
      :errors="computedErrors"
      :hasDate="false"
      :small="small"
      :step="step"
      :min="min"
      :max="max"
      :minlength="minlength"
      :maxlength="maxlength"
      :pattern="pattern"
      @blur="onBlur"
      @focus="$emits('focus')"
    >
      <template #icon-start>
        <slot name="icon-start"></slot>
      </template>
      <template #icon-end>
        <slot name="icon-end"></slot>
      </template>
    </RawInputText>
  </field>
</template>

<script lang="ts" setup>
import { computed, onMounted, type Ref, ref, watch } from 'vue';
import RawInputText from '@/components/form/RawInputText.vue';
import { Caption, InputType, StatusType } from '@/models';
import type { FieldComponentProps } from '@/plugins/field/field-component.model';

export interface Props extends FieldComponentProps {
  inputType?:
    | InputType.TEXT
    | InputType.NUMBER
    | InputType.DATE
    | InputType.DATETIME
    | InputType.TIME;
  modelValue: string | number;
  placeholder?: string;
  min?: number;
  max?: number;
  minlength?: number;
  maxlength?: number;
  step?: number;
  pattern?: string | RegExp;
  patternMessage?: string;
}

const props = withDefaults(defineProps<Props>(), {
  inputType: InputType.TEXT,
  label: '',
  required: false,
  disabled: false,
  small: false,
});

const input: Ref<HTMLElement | null> = ref(null);
const onChangeErrors: Ref<(string | Caption)[]> = ref([]);
const onChangeCaptions: Ref<(string | Caption)[]> = ref([]);
const internalErrors: Ref<(string | Caption)[]> = ref([]);

const computedErrors: Ref<(string | Caption)[]> = computed(() => {
  return props.errors
    ? onChangeErrors.value.concat(props.errors, internalErrors.value)
    : onChangeErrors.value.concat(internalErrors.value);
});
const computedCaptions: Ref<(string | Caption)[]> = computed(() => {
  return props.captions
    ? onChangeCaptions.value.concat(props.captions)
    : onChangeCaptions.value;
});

onMounted(() => {
  internalErrors.value = [];
  onChangeErrors.value = [];
  onChangeCaptions.value = [];
  checkPattern();
  checkLimit();
});

const $emits = defineEmits<{
  'update:modelValue': [value: string];
  blur: [];
  focus: [];
}>();

const onInput = (e: Event) => {
  if (!props.disabled)
    $emits('update:modelValue', (e?.target as HTMLInputElement).value);
};

const checkPattern = () => {
  const hasValue = props.modelValue !== null && props.modelValue !== undefined;
  if (hasValue && props.pattern) {
    const regex = new RegExp(props.pattern);
    if (!regex.test(String(props.modelValue))) {
      internalErrors.value.push(
        props.patternMessage ||
          `La valeur ne correspond pas au motif attendu ( ${props.pattern} )`,
      );
    }
  }
};

const checkLimit = () => {
  const hasValue = props.modelValue !== null && props.modelValue !== undefined;
  if (
    hasValue &&
    props.inputType === InputType.TEXT &&
    typeof props.modelValue === 'string'
  ) {
    if (
      props.minlength !== undefined &&
      props.modelValue.length < props.minlength
    ) {
      onChangeErrors.value.push(
        'Longueur inférieur au minimum autorisé (min:' + props.minlength + ')',
      );
    }
    if (props.maxlength !== undefined) {
      if (props.modelValue.length > props.maxlength) {
        onChangeErrors.value.push(
          `Longueur supérieure au maximum autorisé (max:${props.maxlength})`,
        );
      } else if (props.modelValue.length === props.maxlength) {
        onChangeCaptions.value.push({
          label: `Attention, vous avez atteint la limite de ${props.maxlength} caractères.`,
          mode: StatusType.Warning,
        });
      }
    }
  }
};

const onBlur = () => {
  $emits('blur');
  internalErrors.value = [];
  checkPattern();
};

const setFocus = () => {
  if (input.value) {
    (input.value as any).setFocus();
  }
};

defineExpose({
  setFocus,
});

watch(
  () => props.modelValue,
  () => {
    onChangeErrors.value = [];
    onChangeCaptions.value = [];
    checkLimit();
  },
);
</script>
