<script setup lang="ts">
import { computed, ref, useSlots, useTemplateRef } from 'vue'
import { fetchUpload } from '@/apis/upload'
import ImageCropper from '@/components/image-cropper/image-cropper.vue'

interface Emit {
  (e: 'formatError'): void
  (e: 'oversize'): void
  (e: 'update:imageUrl', value: string): void
}

const props = defineProps({
  imageUrl: {
    type: String,
    default: '',
  },
  maxImageSize: {
    type: Number,
    default: 1024 * 1024 * 3,
  },
  listType: {
    type: Array,
    default: () => ['jpg', 'png', 'jpeg', 'gif'],
  },
  width: {
    type: Number,
    default: 72,
  },
  height: {
    type: Number,
    default: 72,
  },
})

const emit = defineEmits<Emit>()

const slots = useSlots()

const imageCropperRef = useTemplateRef<InstanceType<typeof ImageCropper> | null>('imageCropperRef')

const uploadLoading = ref(false)
const isShowImageMask = ref(false)

const uploadImageUrl = computed({
  get() {
    return props.imageUrl
  },
  set(value: string) {
    emit('update:imageUrl', value)
  },
})

const uploadImageType = computed(() => {
  return props.listType.map((typeItem) => 'image/' + typeItem).join(',')
})

async function handleUploadImage(event: any) {
  const e = window.event || event
  const file = e.target.files[0]

  if (!props.listType.includes(file.type.split('/')[1])) {
    emit('formatError')
    return
  }

  if (file.size > props.maxImageSize) {
    emit('oversize')
    return
  }

  if (imageCropperRef.value && file) {
    const URL = window.URL || window.webkitURL
    imageCropperRef.value.cropImage(URL.createObjectURL(file)).then(async (file) => {
      const formData = new FormData()
      formData.append('file', file)

      uploadLoading.value = true

      const res = await fetchUpload(formData).finally(() => {
        uploadLoading.value = false
        event.target.value = null
      })

      if (res.code === 0) {
        uploadImageUrl.value = res.data as string
      }
    })
  }
}
</script>

<template>
  <div class="relative overflow-hidden rounded-lg" :style="{ height: height + 'px', width: width + 'px' }">
    <NSpin :show="uploadLoading" class="h-full w-full">
      <label
        class="hover:border-blue flex cursor-pointer flex-col items-center justify-center rounded-lg"
        :class="uploadImageUrl ? '' : 'border border-gray-200'"
        :style="{ height: height + 'px', width: width + 'px' }"
        for="upload"
      >
        <i v-if="!uploadLoading" class="iconfont icon-add" />
      </label>
    </NSpin>

    <div
      v-show="uploadImageUrl"
      class="absolute left-0 top-0 h-full w-full"
      @mouseenter="isShowImageMask = true"
      @mouseleave="isShowImageMask = false"
    >
      <img :src="uploadImageUrl" class="h-full w-full object-cover" />

      <label
        v-show="isShowImageMask"
        class="absolute left-0 top-0 flex h-full w-full cursor-pointer items-center justify-center bg-[#151b2680]"
        for="upload"
      >
        <slot v-if="slots.mask" name="mask" />
        <i v-else class="iconfont icon-edit text-white" />
      </label>

      <input id="upload" type="file" :accept="uploadImageType" class="hidden" @change="handleUploadImage" />
    </div>

    <ImageCropper ref="imageCropperRef" />
  </div>
</template>
