<script lang="ts" setup>
const props = withDefaults(defineProps<{
  modelValue?: File
  /** The image src before change */
  original?: string
  /** Allowed file types */
  allowedFileTypes?: string[]
  /** Allowed file size */
  allowedFileSize?: number

  imgClass?: string

  loading?: boolean
}>(), {
  allowedFileTypes: () => ['image/jpeg', 'image/png'],
  allowedFileSize: 1024 * 1024 * 5, // 5 MB
})
const emits = defineEmits<{
  (event: 'update:modelValue', value: File): void
  (event: 'error', code: number, message: string): void
}>()

const vmFile = useVModel(props, 'modelValue', emits, { passive: true })

const { t } = useI18n()

const elInput = ref<HTMLInputElement>()

function clearInput() {
  if (elInput.value)
    elInput.value.value = ''
}

function selectImage(e: Event) {
  const target = e.target as HTMLInputElement
  const image = target.files?.[0]
  if (!image) {
    vmFile.value = image
  }
  else if (!props.allowedFileTypes.includes(image.type)) {
    emits('error', 1, t('error.unsupported_file_format'))
    clearInput()
  }
  else if (image.size > props.allowedFileSize) {
    emits('error', 2, t('error.file_size_cannot_exceed_n_mb', [5]))
    clearInput()
  }
  else {
    vmFile.value = image
    clearInput()
  }
}

const defaultImage = computed(() => props.original || '')
/** Preview of selected images */
const previewImage = ref('')
/** The current images on display */
const imageSrc = computed<string>(() => previewImage.value || defaultImage.value)

// Update the preview image when the input file change
watch(vmFile, (image, _, onCleanup) => {
  let expired = false
  onCleanup(() => expired = true)

  if (image) {
    const reader = new FileReader()
    reader.readAsDataURL(image)
    reader.onload = (e) => {
      if (expired)
        return
      previewImage.value = e.target?.result as string
    }
  }
  else {
    previewImage.value = ''
    clearInput()
  }
})

defineExpose({
  clearInput,
})
</script>

<template>
  <label
    class="bg-slate-500/10 focus-within:(outline outline-primary)"
    relative
    flex justify-center items-center
    cursor-pointer
    of-hidden
  >
    <img
      v-if="imageSrc"
      :src="imageSrc"
      :class="imgClass || ''"
      object-cover
      w-full
      h-full
    >
    <div absolute bg="black/50" text-white rounded-full text-xl w12 h12 flex justify-center items-center hover="bg-black/40 text-primary">
      <div i-ri:upload-line />
    </div>

    <div
      v-if="loading"
      absolute inset-0
      bg="black/30" text-white
      flex justify-center items-center
    >
      <div class="i-ri:loader-4-line animate-spin animate-duration-[2.5s]" text-4xl />
    </div>
    <input
      ref="elInput"
      type="file"
      absolute opacity-0 inset-0 z--1
      :accept="allowedFileTypes.join(',')"
      @change="selectImage"
    >
  </label>
</template>