<script lang="ts" setup>
import { fileOpen } from 'browser-fs-access'
import type { FileWithHandle } from 'browser-fs-access'

const props = withDefaults(defineProps<{
  /** 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 emit = defineEmits<{
  (event: 'pick', value: FileWithHandle): void
  (event: 'error', code: number, message: string): void
}>()

const { modelValue: file } = defineModels<{
  modelValue: FileWithHandle | null
}>()

const { t } = useI18n()

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)

async function pickImage() {
  if (process.server)
    return
  const image = await fileOpen({
    description: 'Image',
    mimeTypes: props.allowedFileTypes,
  })

  if (!props.allowedFileTypes.includes(image.type)) {
    emit('error', 1, t('error.unsupported_file_format'))
    return
  }
  else if (image.size > props.allowedFileSize) {
    emit('error', 2, t('error.file_size_cannot_exceed_n_mb', [5]))
    return
  }

  file.value = image
  emit('pick', file.value)
}

watch(file, (image, _, onCleanup) => {
  let expired = false
  onCleanup(() => expired = true)

  if (!image) {
    previewImage.value = ''
    return
  }
  const reader = new FileReader()
  reader.readAsDataURL(image)
  reader.onload = (evt) => {
    if (expired)
      return
    previewImage.value = evt.target?.result as string
  }
})
</script>

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

    <span
      v-if="loading"
      absolute inset-0
      bg="black/30" text-white
      flex justify-center items-center
    >
      <span class="animate-spin animate-duration-[2.5s] preserve-3d">
        <span block i-ri:loader-4-line text-4xl />
      </span>
    </span>
  </label>
</template>