<template>
  <div class="file-upload-container">
    <el-card class="upload-card">
      <template #header>
        <div class="card-header">
          <span>文件上传</span>
          <el-tag size="small">{{ tip }}</el-tag>
        </div>
      </template>

      <!-- 拖拽上传区域 -->
      <el-upload
        ref="uploadRef"
        v-model:file-list="fileList"
        class="upload-demo"
        :action="uploadUrl"
        :headers="uploadHeaders"
        :on-preview="handlePreview"
        :on-remove="handleRemove"
        :on-success="handleSuccess"
        :on-error="handleError"
        :on-progress="handleProgress"
        :on-change="handleChange"
        :before-upload="beforeUpload"
        :on-exceed="handleExceed"
        :limit="limit"
        :accept="accept"
        :auto-upload="autoUpload"
        :drag="drag"
        :multiple="multiple"
        :data="uploadData"
        name="file"
      >
        <el-icon class="el-icon--upload">
          <upload-filled />
        </el-icon>
        <div class="el-upload__text">
          将文件拖到此处，或<em>点击上传</em>
        </div>
        <template #tip>
          <div class="el-upload__tip">
            <slot name="tip">
              支持上传图片、文档、压缩包等文件，单个文件不超过 {{ maxSize }}MB
            </slot>
          </div>
        </template>
      </el-upload>

      <!-- 进度显示 -->
      <div v-if="showProgress && uploadingFiles.length > 0" class="upload-progress">
        <div v-for="file in uploadingFiles" :key="file.uid" class="progress-item">
          <div class="file-info">
            <el-icon>
              <document />
            </el-icon>
            <span class="file-name">{{ file.name }}</span>
            <span class="file-size">{{ formatSize(file.size) }}</span>
          </div>
          <el-progress
            :percentage="file.percentage || 0"
            :status="file.status"
            :stroke-width="8"
          >
            <span class="progress-text">{{ file.percentage || 0 }}%</span>
          </el-progress>
        </div>
      </div>

      <!-- 图片预览 -->
      <div v-if="showImagePreview && imageFiles.length > 0" class="image-preview">
        <div
          v-for="file in imageFiles"
          :key="file.uid"
          class="preview-item"
          @click="previewImage(file)"
        >
          <el-image
            :src="file.url"
            fit="cover"
            class="preview-image"
          >
            <template #error>
              <div class="image-slot">
                <el-icon><picture-filled /></el-icon>
              </div>
            </template>
          </el-image>
          <div class="preview-actions">
            <el-icon class="delete-icon" @click.stop="handleRemove(file)">
              <delete />
            </el-icon>
          </div>
        </div>
      </div>

      <!-- 操作按钮 -->
      <div v-if="!autoUpload" class="upload-actions">
        <el-button @click="clearFiles">清空</el-button>
        <el-button type="primary" :loading="uploading" @click="submitUpload">
          {{ uploading ? '上传中...' : '开始上传' }}
        </el-button>
      </div>
    </el-card>

    <!-- 图片预览对话框 -->
    <el-dialog v-model="previewDialogVisible" title="图片预览" width="80%">
      <el-image :src="previewImageUrl" fit="contain" />
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElMessage, ElMessageBox, type UploadFile, type UploadFiles, type UploadProgressEvent, type UploadRequestOptions } from 'element-plus'
import {
  UploadFilled,
  Document,
  PictureFilled,
  Delete
} from '@element-plus/icons-vue'
import { getToken } from '@/utils/auth'
import { formatFileSize } from '@/utils/file'

interface Props {
  // 上传地址
  action?: string
  // 是否显示进度
  showProgress?: boolean
  // 是否显示图片预览
  showImagePreview?: boolean
  // 是否拖拽上传
  drag?: boolean
  // 是否多选
  multiple?: boolean
  // 是否自动上传
  autoUpload?: boolean
  // 最大上传数量
  limit?: number
  // 最大文件大小（MB）
  maxSize?: number
  // 接受的文件类型
  accept?: string
  // 上传附带的额外参数
  data?: Record<string, any>
}

const props = withDefaults(defineProps<Props>(), {
  action: '/api/v1/files/upload',
  showProgress: true,
  showImagePreview: true,
  drag: true,
  multiple: true,
  autoUpload: true,
  limit: 10,
  maxSize: 100,
  accept: '',
  data: () => ({})
})

const emit = defineEmits<{
  'update:file-list': [files: UploadFiles]
  'upload-success': [response: any, file: UploadFile]
  'upload-error': [error: Error, file: UploadFile]
  'upload-progress': [event: UploadProgressEvent, file: UploadFile]
}>()

// 上传组件ref
const uploadRef = ref()

// 文件列表
const fileList = ref<UploadFiles>([])
const uploadingFiles = ref<UploadFiles>([])
const imageFiles = ref<UploadFiles>([])

// 上传状态
const uploading = ref(false)

// 预览
const previewDialogVisible = ref(false)
const previewImageUrl = ref('')

// 上传地址
const uploadUrl = computed(() => {
  const baseURL = import.meta.env.VITE_API_BASE_URL || ''
  return baseURL + props.action
})

// 上传请求头
const uploadHeaders = computed(() => ({
  Authorization: `Bearer ${getToken()}`
}))

// 上传额外数据
const uploadData = computed(() => props.data)

// 提示信息
const tip = computed(() => {
  const currentCount = fileList.value.length
  return `${currentCount}/${props.limit}`
})

// 上传前校验
const beforeUpload = (file: File) => {
  // 检查文件大小
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLtMaxSize) {
    ElMessage.error(`上传文件大小不能超过 ${props.maxSize}MB!`)
    return false
  }

  // 检查文件类型
  if (props.accept) {
    const acceptedTypes = props.accept.split(',').map(t => t.trim())
    const fileExt = `.${file.name.split('.').pop()}`
    const isValidType = acceptedTypes.some(type => {
      if (type.startsWith('.')) {
        return fileExt === type
      }
      return file.type.includes(type)
    })

    if (!isValidType) {
      ElMessage.error('不支持的文件类型!')
      return false
    }
  }

  return true
}

// 文件状态改变
const handleChange = (file: UploadFile, files: UploadFiles) => {
  emit('update:file-list', files)

  // 分类文件
  uploadingFiles.value = files.filter(f => f.status === 'uploading')
  imageFiles.value = files.filter(f =>
    f.raw?.type.startsWith('image/') && f.url
  )
}

// 超出限制
const handleExceed = () => {
  ElMessage.warning(`最多只能上传 ${props.limit} 个文件`)
}

// 上传进度
const handleProgress = (event: UploadProgressEvent, file: UploadFile) => {
  emit('upload-progress', event, file)
}

// 上传成功
const handleSuccess = (response: any, file: UploadFile) => {
  ElMessage.success('上传成功')
  emit('upload-success', response, file)
}

// 上传失败
const handleError = (error: Error, file: UploadFile) => {
  ElMessage.error('上传失败: ' + error.message)
  emit('upload-error', error, file)
}

// 预览文件
const handlePreview = (file: UploadFile) => {
  if (file.url) {
    window.open(file.url, '_blank')
  }
}

// 移除文件
const handleRemove = (file: UploadFile) => {
  const index = fileList.value.findIndex(f => f.uid === file.uid)
  if (index > -1) {
    fileList.value.splice(index, 1)
  }
}

// 预览图片
const previewImage = (file: UploadFile) => {
  if (file.url) {
    previewImageUrl.value = file.url
    previewDialogVisible.value = true
  }
}

// 手动上传
const submitUpload = () => {
  uploadRef.value?.submit()
  uploading.value = true
  setTimeout(() => {
    uploading.value = false
  }, 3000)
}

// 清空文件
const clearFiles = () => {
  uploadRef.value?.clearFiles()
  fileList.value = []
  uploadingFiles.value = []
  imageFiles.value = []
}

// 格式化文件大小
const formatSize = (size: number) => {
  return formatFileSize(size)
}

// 暴露方法
defineExpose({
  clearFiles,
  submitUpload
})
</script>

<style scoped lang="scss">
.file-upload-container {
  .upload-card {
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
  }

  :deep(.el-upload) {
    width: 100%;
  }

  :deep(.el-upload-dragger) {
    width: 100%;
    height: 200px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }

  .upload-progress {
    margin-top: 20px;

    .progress-item {
      margin-bottom: 15px;

      .file-info {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 8px;

        .file-name {
          flex: 1;
          font-size: 14px;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .file-size {
          font-size: 12px;
          color: var(--el-text-color-secondary);
        }
      }

      .progress-text {
        font-size: 12px;
      }
    }
  }

  .image-preview {
    margin-top: 20px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 12px;

    .preview-item {
      position: relative;
      width: 120px;
      height: 120px;
      border-radius: 4px;
      overflow: hidden;
      cursor: pointer;
      transition: all 0.3s;

      &:hover {
        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);

        .preview-actions {
          opacity: 1;
        }
      }

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

      .preview-actions {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
        opacity: 0;
        transition: opacity 0.3s;

        .delete-icon {
          color: white;
          font-size: 24px;
        }
      }
    }
  }

  .upload-actions {
    margin-top: 20px;
    display: flex;
    justify-content: flex-end;
    gap: 12px;
  }

  .image-slot {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    background: var(--el-fill-color-light);
    color: var(--el-text-color-secondary);
    font-size: 30px;
  }
}
</style>
