<script setup lang="ts">
/**
 * 通用图片上传组件
 * 内部调用 /api/file/upload，组件只关心 url 即可
 */
import { ref, watch } from 'vue'
import { Plus, Loading, Picture } from '@element-plus/icons-vue'
import { ElMessage, type UploadProps } from 'element-plus'
import { uploadFileApi, type FileUploadResult } from '@/api/file'

interface Props {
  modelValue?: string                  // 已有的 URL（编辑时回显）
  bizType?: string                      // 业务类型
  listType?: 'text' | 'picture' | 'picture-card'  // el-upload 的展示类型
  maxSize?: number                      // 单文件大小限制（MB）
  accept?: string                       // 接受的文件类型
  disabled?: boolean
  disabledTip?: string                  // 禁用时的提示
  // 返回新 URL 的事件
  // (update:modelValue) 已由 v-model 自动处理
}

const props = withDefaults(defineProps<Props>(), {
  bizType: 'common',
  listType: 'picture-card',
  maxSize: 5,
  accept: 'image/*',
  disabled: false,
  disabledTip: ''
})

const emit = defineEmits<{
  (e: 'update:modelValue', val: string | undefined): void
  (e: 'change', result: FileUploadResult): void
}>()

const loading = ref(false)
const fileUrl = ref<string | undefined>(props.modelValue)

watch(() => props.modelValue, (v) => {
  fileUrl.value = v
})

const headers = {
  // 上传时让 axios 自动加 Content-Type + boundary，这里其实可以不设
}

/** 拦截 el-upload 自动上传，改为手动调接口（更可控） */
const httpRequest: UploadProps['httpRequest'] = async (options) => {
  const file = options.file as File
  if (file.size > props.maxSize * 1024 * 1024) {
    ElMessage.error(`文件大小不能超过 ${props.maxSize}MB`)
    options.onError({ status: 1, message: 'file too large' } as any)
    return
  }
  loading.value = true
  try {
    const res = await uploadFileApi(file, props.bizType)
    fileUrl.value = res.url
    emit('update:modelValue', res.url)
    emit('change', res)
    ElMessage.success('上传成功')
    options.onSuccess(res)
  } catch (e: any) {
    ElMessage.error('上传失败：' + (e?.message || '未知错误'))
    options.onError(e)
  } finally {
    loading.value = false
  }
}

const beforeUpload: UploadProps['beforeUpload'] = (file) => {
  if (file.size > props.maxSize * 1024 * 1024) {
    ElMessage.error(`文件大小不能超过 ${props.maxSize}MB`)
    return false
  }
  return true
}

function handleRemove() {
  fileUrl.value = undefined
  emit('update:modelValue', undefined)
}
</script>

<template>
  <div class="image-uploader">
    <el-upload
      v-if="!disabled"
      :show-file-list="false"
      :list-type="listType"
      :http-request="httpRequest"
      :before-upload="beforeUpload"
      :accept="accept"
      :headers="headers"
      class="uploader-trigger"
    >
      <template v-if="fileUrl">
        <img v-if="listType !== 'text'" :src="fileUrl" class="preview-image" />
        <el-button v-else type="primary" :icon="Picture">已上传，点击重新上传</el-button>
      </template>
      <template v-else>
        <el-icon v-if="loading" class="is-loading"><Loading /></el-icon>
        <el-icon v-else><Plus /></el-icon>
        <div v-if="listType !== 'text'" class="upload-text">点击上传</div>
      </template>
    </el-upload>

    <!-- 禁用态：只读预览 -->
    <div v-else class="disabled-preview">
      <img v-if="fileUrl" :src="fileUrl" class="preview-image" />
      <el-empty v-else description="暂无图片" :image-size="60" />
      <div v-if="disabledTip" class="disabled-tip">{{ disabledTip }}</div>
    </div>

    <!-- 已上传时显示删除按钮（仅非 disabled） -->
    <div v-if="!disabled && fileUrl" class="actions">
      <el-button text type="danger" size="small" @click="handleRemove">移除</el-button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.image-uploader {
  display: inline-block;
}

.uploader-trigger {
  :deep(.el-upload) {
    border: 1px dashed #dcdfe6;
    border-radius: 8px;
    cursor: pointer;
    transition: border-color 0.2s;
    background: $bg-page;
    overflow: hidden;

    &:hover {
      border-color: $brand-primary;
    }
  }

  :deep(.el-upload--picture-card) {
    width: 120px;
    height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 4px;
  }

  .preview-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .upload-text {
    font-size: 12px;
    color: $color-text-secondary;
  }
}

.disabled-preview {
  width: 120px;
  height: 120px;
  border: 1px solid $color-border-light;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background: $bg-page;
  overflow: hidden;

  .preview-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.actions {
  margin-top: 6px;
  text-align: center;
}
</style>
