<script setup lang="ts">
import type { UploadCustomRequestOptions } from 'naive-ui'
import { NButton, NSpin, NUpload, useMessage } from 'naive-ui'
import { ref } from 'vue'
import { getAliOssTokenAxios } from '@/service/api/upload'
import { browserPathJoin } from '@/utils/date'
import { initOSSClient, uploadFileToOSS } from '@/utils/oss'

const props = withDefaults(defineProps<{
  /** 图片地址 */
  modelValue?: string
  /** 上传路径 */
  path?: string // 上传路径，默认 temp/ 目录，同名文件会覆盖
  /** 提示文字 */
  hint?: string
  /** 最大文件大小 (KB) */
  maxSize?: number
  /** 接受的文件类型 */
  accept?: string
  /** 模式: default-大图上传(带文字), mini-小图上传(仅图标) */
  variant?: 'default' | 'mini'
}>(), {
  hint: '支持 JPG/PNG 格式',
  accept: 'image/*',
  variant: 'default',
})

const emit = defineEmits(['update:modelValue'])

const loading = ref(false)
const message = useMessage()

async function customRequest({ file, onFinish, onError }: UploadCustomRequestOptions) {
  try {
    // 校验文件大小
    if (props.maxSize && file.file) {
      const sizeKB = file.file.size / 1024
      if (sizeKB > props.maxSize) {
        const maxSizeText = props.maxSize >= 1024 ? `${(props.maxSize / 1024).toFixed(2)}MB` : `${props.maxSize}KB`
        throw new Error(`文件大小不能超过 ${maxSizeText}`)
      }
    }

    loading.value = true
    // 1. 获取上传凭证
    const { data: tokenData, error: tokenError } = await getAliOssTokenAxios()
    if (tokenError || !tokenData) {
      throw new Error('获取上传凭证失败')
    }

    // 2. 初始化 OSS 客户端
    const client = initOSSClient(tokenData.data || {})

    // 3. 准备路径
    let path = props.path || `temp/${Date.now()}`
    path = browserPathJoin(path, file.name)

    // 4. 上传文件
    await uploadFileToOSS(client, file.file as File, path)

    // 5. 获取 URL
    const url = browserPathJoin(import.meta.env.VITE_BASE_OSS_URL, path)

    emit('update:modelValue', url)
    onFinish()
  }
  catch (error: any) {
    message.error(error.message || '上传失败')
    onError()
  }
  finally {
    loading.value = false
  }
}

function handleRemove() {
  emit('update:modelValue', '')
}
</script>

<template>
  <NUpload
    :accept="accept"
    :show-file-list="false"
    :custom-request="customRequest"
    class="block"
    :class="{ 'w-full': props.variant === 'default' }"
  >
    <div
      class="relative flex flex-col cursor-pointer items-center justify-center overflow-hidden transition-all hover:bg-gray-100"
      :class="[
        props.variant === 'default' ? 'h-[400px] w-full rounded-3xl bg-[#F5F8FF]' : 'h-full w-full bg-[#F5F8FF] rounded-lg',
        !modelValue && props.variant === 'default' ? 'border-2 border-dashed border-gray-300' : '',
        !modelValue && props.variant === 'mini' ? 'border border-dashed border-gray-300' : '',
      ]"
    >
      <div v-if="loading" class="absolute inset-0 z-50 flex items-center justify-center bg-white/50">
        <NSpin :size="props.variant === 'mini' ? 'small' : 'large'" />
      </div>

      <template v-else>
        <div
          v-if="modelValue"
          class="group absolute inset-0 flex items-center justify-center bg-black/50 opacity-0 transition-opacity hover:opacity-100"
        >
          <div class="flex gap-4">
            <!-- NUpload 的 trigger 会自动处理点击事件 -->
            <NButton v-if="props.variant === 'default'" ghost color="#fff" size="small">
              更换
            </NButton>
            <div v-else class="cursor-pointer text-xs text-white">
              更换
            </div>
            <NButton v-if="props.variant === 'default'" ghost color="#fff" size="small" @click.stop="handleRemove">
              删除
            </NButton>
          </div>
        </div>

        <img
          v-if="modelValue"
          :src="modelValue"
          class="h-full w-full object-cover"
          alt="uploaded"
        >

        <div v-else class="flex flex-col items-center justify-center text-gray-400">
          <template v-if="props.variant === 'default'">
            <slot name="icon">
              <icon-ic-baseline-upload class="mb-4 text-6xl" />
            </slot>
            <div class="text-lg text-gray-600 font-medium">
              点击上传
            </div>
            <div class="mt-2 text-sm text-gray-400">
              {{ hint }}
            </div>
          </template>
          <template v-else>
            <icon-ic-baseline-upload class="text-xl" />
          </template>
        </div>
      </template>
    </div>
  </NUpload>
</template>

<style scoped>
:deep(.n-upload-trigger) {
  width: 100%;
  height: 100%;
}
</style>
