<template>
  <div class="block">
    <div class="block__wrapper">
      <label v-if="label" class="block__label">{{ label }}</label>
      <div class="block__wrapper-inner">
        <input
            :placeholder="placeholder"
            :type="isPasswordVisible"
            :value="modelValue"
            @input="onInput"
            @keydown="numberOnly ? onlyNumbersKeydown($event) : null"
            class="block__input"
            :inputmode="inputMode || 'text'"
        >
        <button
            @click.prevent="togglePasswordVisible"
            class="block__eyes"
            v-if="type === 'password' && String(modelValue).length > 0"
        >
          <icon v-if="isPasswordVisible === 'password'" name="mdi:eye-off-outline" />
          <icon v-else name="mdi:eye-outline" />
        </button>
      </div>
    </div>
    <p v-if="!isValid" class="block__error">{{ errorMessage }}</p>
  </div>
</template>

<script setup lang="ts">
type Rule = (value: string) => boolean | string;

const emit = defineEmits<{
  (e: 'update:modelValue', value: string | number): void;
}>();
const props = defineProps<{
  type: string;
  placeholder: string;
  modelValue?: string | number;
  rules?: Rule[];
  label?: string;
  numberOnly?: boolean;
  inputMode?: "text" | "email" | "search" | "tel" | "url" | "none" | "numeric" | "decimal";
}>();

const isPasswordVisible = ref<string>(props.type);
const isValid = ref<boolean>(true);
const errorMessage = ref<string>('');

function togglePasswordVisible() {
  isPasswordVisible.value =
    isPasswordVisible.value === 'password' ? 'text' : 'password';
}

const onlyNumbersKeydown = (event: KeyboardEvent) => {
  if (!/^\d$/.test(event.key) &&
      !['ArrowLeft', 'ArrowRight', 'Backspace', 'Delete', 'Tab', 'Home', 'End'].includes(event.key)) {
    event.preventDefault();
  }
};

function onInput(e: Event) {
  const target = e.target as HTMLInputElement;
  let value = target.value;

  if (props.numberOnly) {
    const digitsOnly = value.replace(/\D/g, '');
    if (digitsOnly !== value) {
      target.value = digitsOnly;
      value = digitsOnly;
    }
  }

  let valid = true;
  errorMessage.value = '';

  props.rules?.forEach((rule) => {
    const result = rule(value);
    if (result !== true) {
      valid = false;
      errorMessage.value = String(result);
    }
  })

  isValid.value = valid;

  emit('update:modelValue', props.numberOnly ? Number(value) : value);
}
</script>

<style lang="scss" scoped>
.block {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  position: relative;

  &__wrapper {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 10px;

    &-inner {
      position: relative;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
  }

  &__label {
    color: #374151;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: -0.5px;
  }

  &__input {
    width: 100%;
    padding: 14px 12px;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    background-color: $white;

    color: #1f1f1f;
    font-size: 16px;
    font-weight: 400;
    letter-spacing: -0.5px;

    &::placeholder {
      color: #868686;
    }
  }

  &__eyes {
    cursor: pointer;
    position: absolute;
    right: 20px;
    top: 50%;
    transform: translateY(-50%);
    background-color: transparent;
    display: grid;
    place-items: center;
    font-size: 18px;
    color: #838383;
  }

  &__error {
    color: $error;
    font-size: 12px;
    font-weight: 500;
    animation: fadeInUp 0.3s ease;

    @keyframes fadeInUp {
      0% {
        opacity: 0;
        transform: translateY(-50%);
      }
      100% {
        opacity: 1;
        transform: translateY(0);
      }
    }
  }
}
</style>