<template>
  <view class="im-upload">
    <!-- 上传区域 -->
    <view class="im-upload__area" :class="[
      `im-upload__area--${type}`,
      { 'im-upload__area--disabled': disabled },
      { 'im-upload__area--readonly': readonly },
      { 'im-upload__area--drag-over': dragOver }
    ]" @tap="handleUploadTap" @touchmove="handleDragOver" @touchend="handleDragLeave">
      <!-- 按钮类型 -->
      <template v-if="type === 'button' && maxCount !== fileList.length">
        <im-button :type="buttonType" :size="buttonSize" :disabled="disabled" :loading="uploading">
          <!-- <view>  -->
          <text v-if="!uploading" class="im-upload__icon">+</text>
          <text class="im-upload__text">{{ uploading ? '上传中...' : buttonText }}</text>
          <!-- </view> -->
        </im-button>
      </template>

      <!-- 卡片类型 -->
      <template v-else-if="type === 'card' && maxCount !== fileList.length">
        <view class="im-upload__card">
          <text class="im-upload__card-icon">+</text>
          <text class="im-upload__card-text">{{ cardText }}</text>
          <text v-if="hint" class="im-upload__card-hint">{{ hint }}</text>
        </view>
      </template>

      <!-- 头像类型 -->
      <template v-else-if="type === 'avatar'">
        <view class="im-upload__avatar" :style="{ width: avatarSize + 'rpx' }">
          <im-avatar :url="fileList[0]?.url" :size="avatarSize" :radius="avatarRadius">
            <template v-if="!fileList[0]?.url">
              <im-icon name="user" :size="avatarSize" color="#8c939d" />
            </template>
          </im-avatar>
          <text v-if="avatarText" class="im-upload__avatar-text">{{ avatarText }}</text>
        </view>
        <im-icon v-if="showAvatarCamera && !avatarText" name="camera" :color="avatarCameraColor" size="36"
          style="position: absolute; top: calc(-1 * var(0px, 0px));right: calc(-1 * var(0px, 0px));transform: translate(200%, -100%);" />
      </template>

      <!-- 拖拽区域类型 -->
      <template v-else-if="type === 'drag'">
        <view class="im-upload__drag">
          <text class="im-upload__drag-icon">📁</text>
          <text class="im-upload__drag-text">{{ dragText }}</text>
          <text v-if="hint" class="im-upload__drag-hint">{{ hint }}</text>
        </view>
      </template>

      <slot v-else></slot>
    </view>

    <!-- 文件列表 -->
    <view class="im-upload__list">
      <view v-if="innerShowList && fileList.length > 0" v-for="(file, index) in fileList" :key="file.uid"
        class="im-upload__item" :class="`im-upload__item--${listType}`">
        <!-- 图片列表 -->
        <template v-if="listType === 'picture'">
          <view class="im-upload__item-preview">
            <image v-if="file.type?.startsWith('image')" class="im-upload__item-image" :src="file.url || file.thumbUrl"
              mode="aspectFill" @tap="handlePreview(file)" />
            <view v-else class="im-upload__item-file">
              <text class="im-upload__item-file-icon">📄</text>
              <text class="im-upload__item-file-name" @tap="handlePreview(file)">
                {{ file.name }}
              </text>
            </view>

            <!-- 上传状态 -->
            <view v-if="file.status === 'uploading'" class="im-upload__item-status">
              <view class="im-upload__item-progress">
                <view class="im-upload__item-progress-bar" :style="{ width: `${file.progress || 0}%` }"></view>
              </view>
              <text class="im-upload__item-percent">{{ file.progress || 0 }}%</text>
            </view>

            <view v-else-if="file.status === 'error'" class="im-upload__item-error">
              <text class="im-upload__item-error-text">上传失败</text>
              <text v-if="!readonly" class="im-upload__item-retry" @tap="handleRetry(file, index)">
                重试
              </text>
            </view>

            <!-- 操作按钮 -->
            <view class="im-upload__item-actions">
              <text v-if="!readonly && removable" class="im-upload__item-action" @tap="handleRemove(file, index)">
                ✕
              </text>
            </view>
          </view>
        </template>

        <!-- 文本列表 -->
        <template v-else>
          <view class="im-upload__item-text">
            <text class="im-upload__item-name">{{ file.name }}</text>
            <text class="im-upload__item-size">{{ formatSize(file.size) }}</text>
            <view class="im-upload__item-status-container">
              <text class="im-upload__item-status-text" :class="`im-upload__item-status--${file.status}`">
                {{ getStatusText(file.status) }}
              </text>
              <text v-if="file.status === 'uploading'" class="im-upload__item-percent-text">
                {{ file.progress }}%
              </text>
            </view>
          </view>

          <!-- 操作按钮 -->
          <view v-if="!readonly" class="im-upload__item-text-actions">
            <text v-if="file.status === 'error'" class="im-upload__item-text-action" @tap="handleRetry(file, index)">
              重试
            </text>
            <text v-if="file.status === 'done' && previewable" class="im-upload__item-text-action"
              @tap="handlePreview(file)">
              预览
            </text>
            <text v-if="removable" class="im-upload__item-text-action im-upload__item-text-action--remove"
              @tap="handleRemove(file, index)">
              删除
            </text>
          </view>
        </template>
      </view>

      <!-- 上传区域（默认类型） -->
      <view v-if="!type && maxCount !== fileList.length" class="im-upload__default" @tap="handleUploadTap">
        <im-icon customClass="im-upload__default-icon" :size="100" color="#dcdfe6" type="custom" name="+" />
      </view>
    </view>

    <!-- 上传提示 -->
    <view v-if="showTip" class="im-upload__tip">
      <text class="im-upload__tip-text">{{ tip }}</text>
      <text v-if="fileList.length > 0" class="im-upload__tip-count">
        已选择 {{ fileList.length }} 个文件
      </text>
    </view>

    <!-- 操作按钮 -->
    <view v-if="showActions && fileList.length > 0 && !readonly" class="im-upload__actions">
      <im-button v-if="showUploadAll" type="primary" size="small" :loading="uploadingAll"
        :disabled="uploadingAll || disabled" @click="handleUploadAll">
        {{ uploadingAll ? '上传中...' : '全部上传' }}
      </im-button>

      <im-button v-if="showClear" type="default" size="small" :disabled="disabled" @click="handleClearAll">
        清空列表
      </im-button>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import ImButton from '../im-button/im-button.vue'
import { chooseFile } from './utils/file-chooser'
import { isFileTypeAccepted } from './utils/file-validator'
import { createFileFromUniFile } from './utils/file-adapter'
import { universalUploadFile } from './utils/upload'
import type { UploadFile, UploadProps, UploadEmits } from '../../types/components/upload'

// 定义 Props 默认值
const props = withDefaults(defineProps<UploadProps>(), {
  modelValue: () => [],

  type: undefined, // 'button',

  buttonText: '上传文件',
  buttonType: 'primary',
  buttonSize: 'medium',

  cardText: '点击上传',

  avatarText: '',
  avatarSize: 120,
  avatarRadius: '50%',
  showAvatarCamera: true,
  avatarCameraColor: 'rgb(208 208 208)',

  dragText: '将文件拖到此处，或点击上传',

  tip: '支持上传图片、文档等文件',
  showTip: false,
  showActions: false,
  showUploadAll: true,
  showClear: true,

  accept: '*',
  multiple: false,
  maxCount: 9,
  maxSize: 10 * 1024 * 1024,

  action: '',
  accessToken: '',
  headers: () => ({}),
  data: () => ({}),
  name: 'file',
  withCredentials: false,
  timeout: 10000,
  autoUpload: true,

  responseFormatter: (response: any) => {
    console.log('responseFormatter', response)
    return {
      url: response?.data?.url || response?.result?.url || response?.url || response?.fileUrl,
      name: response?.data?.fileName || response?.result?.fileName || response?.data?.name || response?.result?.name || response?.name,
      ...response
    }
  },

  showList: true,
  listType: 'picture',
  removable: true,
  previewable: true,

  disabled: false,
  readonly: false
})

// 定义 Emits
const emit = defineEmits<UploadEmits>()

// 响应式状态
const fileList = ref<UploadFile[]>([]) //props.modelValue || []
const dragOver = ref(false)
const uploadingAll = ref(false)

// 上传任务映射
const uploadTasks = new Map<string, UniApp.UploadTask>()

// 监听 modelValue 变化
watch(() => props.modelValue, (val) => {
  if (!val) return

  if (isSingleFile()) {
    fileList.value = [{
      url: String(props.modelValue),
      uid: '',
      name: 'single file',
      size: 0,
      status: 'uploading'
    }]
  }
  else {
    fileList.value = val as UploadFile[]
  }
}, { deep: true })

// 计算属性
const innerShowList = computed(() => {
  if (props.type === 'avatar') {
    return false
  }
  return props.showList
})

const uploading = computed(() => {
  return fileList.value.some(file => file.status === 'uploading')
})

// 生成唯一ID
const generateUid = () => {
  return Date.now() + '-' + Math.random().toString(36).substring(2, 9)
}

// 格式化文件大小
const formatSize = (bytes?: number) => {
  if (!bytes) return '0 B'

  const units = ['B', 'KB', 'MB', 'GB']
  let size = bytes
  let unitIndex = 0

  while (size >= 1024 && unitIndex < units.length - 1) {
    size /= 1024
    unitIndex++
  }

  return `${size.toFixed(1)} ${units[unitIndex]}`
}

// 获取状态文本
const getStatusText = (status?: string) => {
  const map: Record<string, string> = {
    pending: '等待上传',
    uploading: '上传中',
    done: '已完成',
    error: '上传失败'
  }
  return status ? map[status] || '未知' : '未知'
}

// 处理上传点击
const handleUploadTap = async () => {
  if (props.disabled || props.readonly) {
    return
  }

  let accept = getFileType(props.accept) // image, video, all
  let maxSelectable = props.multiple ? Math.max(0, props.maxCount! - fileList.value.length) : 1

  if (props.type === 'avatar') {
    accept = 'image'
    maxSelectable = 1

  }

  if (maxSelectable <= 0) {
    emit('exceed', [])
    uni.showToast({
      title: `最多只能上传${props.maxCount}个文件`,
      icon: 'none'
    })
    return
  }

  try {
    const res = await chooseFile({
      count: maxSelectable,
      type: accept
    })

    for (const tempFile of res.tempFiles as Array<any>) {
      await processFile(tempFile)
    }
  } catch (error) {
    console.error('选择文件失败:', error)
    uni.showToast({
      title: '选择文件失败',
      icon: 'none'
    })
  }
}

// 获取文件类型
const getFileType = (accept: string): 'image' | 'video' | 'all' => {
  if (accept.includes('image')) return 'image'
  if (accept.includes('video')) return 'video'
  return 'all'
}

// 是否仅上传单个文件
const isSingleFile = () => {
  return typeof props.modelValue === 'string' || props.type === 'avatar' || !props.multiple
}

// 处理文件
const processFile = async (uniFile: any) => {
  // 检查文件大小
  if (props.maxSize && uniFile.size > props.maxSize) {
    uni.showToast({
      title: `文件大小不能超过${formatSize(props.maxSize)}`,
      icon: 'none'
    })
    return
  }

  // 检查文件类型
  if (!isFileTypeAccepted(uniFile, props.accept)) {
    uni.showToast({
      title: '不支持的文件类型',
      icon: 'none'
    })
    return
  }

  // 创建文件对象
  const file = createFileFromUniFile(uniFile)

  // 执行上传前钩子
  if (props.beforeUpload) {
    try {
      const result = await Promise.resolve(props.beforeUpload(file))
      if (result === false) {
        return
      }
    } catch (error) {
      console.error('beforeUpload error:', error)
      return
    }
  }

  // 创建上传文件对象
  const uploadFile: UploadFile = {
    uid: generateUid(),
    name: uniFile.name,
    size: uniFile.size,
    type: uniFile.type,
    status: 'pending',
    progress: 0,
    url: uniFile.path,
    thumbUrl: uniFile.type?.startsWith('image') ? uniFile.path : undefined,
    rawFile: uniFile,
    file
  }

  // 添加到文件列表
  if (!props.multiple) {
    fileList.value = [uploadFile]

  } else {
    fileList.value.push(uploadFile)
  }

  emit('select', file)
  updateFileList()

  // 自动上传
  if (props.autoUpload && (props.action || props.customRequest)) {
    await startUpload(uploadFile)
  }
}

// 开始上传
const startUpload = async (uploadFile: UploadFile) => {
  uploadFile.status = 'uploading'
  uploadFile.progress = 0
  emit('uploading', uploadFile)

  // updateFileList()

  try {
    let response: any

    if (props.customRequest) {
      // 使用自定义上传函数
      response = await props.customRequest(
        uploadFile.file!,
        (percent) => {
          uploadFile.progress = percent
          emit('progress', percent, uploadFile)
          updateFileList(uploadFile)
        }
      )
    } else if (props.action) {
      // 使用默认上传
      response = await defaultUpload(uploadFile)
    } else {
      throw new Error('请设置上传地址或自定义上传函数')
    }

    // 服务端失败
    if (response.statusCode !== 200) {
      throw new Error(response.data.error.message)
    }

    // 格式化响应数据
    const formattedResponse = props.responseFormatter(response)

    uploadFile.status = 'done'
    uploadFile.progress = 100
    uploadFile.response = formattedResponse
    uploadFile.url = formattedResponse.url || uploadFile.url

    emit('success', uploadFile)
  } catch (error) {
    uploadFile.status = 'error'
    uploadFile.error = error as Error
    emit('error', error as Error, uploadFile)

    uni.showToast({
      title: `${uploadFile.name} 上传失败`,
      icon: 'none'
    })
  } finally {
    emit('uploaded', uploadFile)
    updateFileList(uploadFile)
  }
}

// 默认上传实现
const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
  let headers = props.headers

  if (props.accessToken) {
    headers = {
      ...headers,
      'Authorization': `Bearer ${props.accessToken}`,
      'AccessToken': props.accessToken,
    }
  }

  return universalUploadFile({
    url: props.action!,
    filePath: uploadFile.rawFile.path,
    name: props.name,
    formData: {
      ...props.data,
      filename: uploadFile.name,
      size: uploadFile.size,
      type: uploadFile.type
    },
    header: headers,
    timeout: props.timeout,
    withCredentials: props.withCredentials
  })
}

// 更新文件列表
const updateFileList = (uploadFile?: UploadFile) => {
  emit('change', [...fileList.value])

  if (isSingleFile()) {
    emit('update:modelValue', String(uploadFile?.url))
  } else {
    emit('update:modelValue', [...fileList.value])
  }
}

// 处理删除
const handleRemove = (file: UploadFile, index: number) => {
  if (props.disabled || props.readonly) {
    return
  }

  // 如果正在上传，先取消上传
  if (file.status === 'uploading') {
    const task = uploadTasks.get(file.uid)
    if (task) {
      task.abort()
      uploadTasks.delete(file.uid)
    }
  }

  fileList.value.splice(index, 1)
  emit('remove', file, index)
  updateFileList()
}

// 处理重试
const handleRetry = async (file: UploadFile, index: number) => {
  if (props.disabled || props.readonly) {
    return
  }

  await startUpload(file)
}

// 处理预览
const handlePreview = (file: UploadFile) => {
  if (!props.previewable) {
    return
  }

  emit('preview', file)

  // 如果是图片，使用 uni.previewImage
  if (file.url && file.type?.startsWith('image')) {
    uni.previewImage({
      urls: [file.url],
      current: file.url
    })
  }
}

// 处理拖拽
const handleDragOver = (e: any) => {
  if (props.type === 'drag' && !props.disabled && !props.readonly) {
    dragOver.value = true
  }
}

const handleDragLeave = (e: any) => {
  dragOver.value = false
}

// 处理全部上传
const handleUploadAll = async () => {
  if (props.disabled || props.readonly || !props.action) {
    return
  }

  const pendingFiles = fileList.value.filter(file =>
    file.status === 'pending' || file.status === 'error'
  )

  if (pendingFiles.length === 0) {
    uni.showToast({
      title: '没有需要上传的文件',
      icon: 'none'
    })
    return
  }

  uploadingAll.value = true

  for (const file of pendingFiles) {
    await startUpload(file)
  }

  uploadingAll.value = false
}

// 处理清空列表
const handleClearAll = () => {
  if (props.disabled || props.readonly) {
    return
  }

  // 取消所有进行中的上传
  uploadTasks.forEach(task => task.abort())
  uploadTasks.clear()

  fileList.value = []
  updateFileList()

  uni.showToast({
    title: '已清空文件列表',
    icon: 'success'
  })
}

// 清空文件列表
const clearFiles = () => {
  handleClearAll()
}

// 取消指定上传
const abortUpload = (uid: string) => {
  const task = uploadTasks.get(uid)
  if (task) {
    task.abort()
    uploadTasks.delete(uid)

    const file = fileList.value.find(f => f.uid === uid)
    if (file) {
      file.status = 'error'
      file.error = new Error('上传已取消')
      updateFileList()
    }
  }
}

// 暴露方法给父组件
defineExpose({
  clearFiles,
  abortUpload,
  uploadAll: handleUploadAll,
  getFiles: () => [...fileList.value]
})
</script>

<style lang="scss" scoped>
.im-upload__area {
  position: relative;

  &--disabled {
    opacity: 0.6;
    pointer-events: none;
  }

  &--drag-over {
    border-color: #409eff !important;
    background-color: #ecf5ff !important;
  }
}

.im-upload__button-content {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8rpx;
}

.im-upload__icon {
  font-size: 32rpx;
  font-weight: bold;
}

.im-upload__text {
  font-size: 28rpx;
}

.im-upload__default {
  margin-top: 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 180rpx;
  height: 180rpx;
  border: 4rpx dashed #dcdfe6;
  border-radius: 20rpx;
  background-color: #fafafa;
  transition: all 0.3s;

  &-icon {
    color: inherit;
  }
}

.im-upload__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60rpx 40rpx;
  border: 2rpx dashed #dcdfe6;
  border-radius: 8rpx;
  background-color: #fafafa;
  transition: all 0.3s;

  &:active {
    border-color: #409eff;
    background-color: #ecf5ff;
  }
}

.im-upload__card-icon {
  font-size: 48rpx;
  color: #8c939d;
  margin-bottom: 20rpx;
}

.im-upload__card-text {
  font-size: 28rpx;
  color: #606266;
}

.im-upload__card-hint {
  font-size: 24rpx;
  color: #909399;
  margin-top: 12rpx;
}

.im-upload__avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20rpx;
}

.im-upload__avatar-text {
  font-size: 26rpx;
  color: #606266;
}

.im-upload__drag {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80rpx 40rpx;
  border: 2rpx dashed #dcdfe6;
  border-radius: 8rpx;
  background-color: #fafafa;
  transition: all 0.3s;

  &:active {
    border-color: #409eff;
    background-color: #ecf5ff;
  }
}

.im-upload__drag-icon {
  font-size: 60rpx;
  margin-bottom: 20rpx;
}

.im-upload__drag-text {
  font-size: 28rpx;
  color: #606266;
  margin-bottom: 12rpx;
}

.im-upload__drag-hint {
  font-size: 24rpx;
  color: #909399;
}

.im-upload__list {
  // margin-top: 20rpx;
  display: flex;
  flex-wrap: wrap;
}

.im-upload__item {
  &--picture {
    margin-top: 20rpx;
    margin-right: 20rpx;
  }

  &--text {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24rpx;
    margin-bottom: 16rpx;
    border: 1rpx solid #ebeef5;
    border-radius: 6rpx;
    background-color: #f5f7fa;
  }
}

.im-upload__item-preview {
  position: relative;
  width: 184rpx;
  height: 184rpx;
  border-radius: 8rpx;
  overflow: hidden;
  border: 1rpx solid #ebeef5;
}

.im-upload__item-image {
  width: 100%;
  height: 100%;
}

.im-upload__item-file {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background-color: #f5f7fa;
}

.im-upload__item-file-icon {
  font-size: 60rpx;
  margin-bottom: 12rpx;
}

.im-upload__item-file-name {
  font-size: 24rpx;
  color: #606266;
  text-align: center;
  padding: 0 16rpx;
  word-break: break-all;
}

.im-upload__item-status {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16rpx;
  background-color: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 24rpx;
}

.im-upload__item-progress {
  width: 100%;
  height: 8rpx;
  background-color: rgba(255, 255, 255, 0.3);
  border-radius: 4rpx;
  overflow: hidden;
  margin-bottom: 8rpx;
}

.im-upload__item-progress-bar {
  height: 100%;
  background-color: #409eff;
  transition: width 0.3s;
}

.im-upload__item-percent {
  font-size: 22rpx;
}

.im-upload__item-error {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16rpx;
  background-color: rgba(255, 77, 79, 0.8);
  color: white;
  font-size: 24rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.im-upload__item-error-text {
  font-size: 22rpx;
}

.im-upload__item-retry {
  font-size: 22rpx;
  text-decoration: underline;
  cursor: pointer;
}

.im-upload__item-actions {
  position: absolute;
  top: 8rpx;
  right: 8rpx;
  display: flex;
  gap: 8rpx;
}

.im-upload__item-action {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48rpx;
  height: 48rpx;
  background-color: rgba(0, 0, 0, 0.6);
  color: white;
  border-radius: 50%;
  font-size: 24rpx;
  cursor: pointer;

  &:active {
    background-color: rgba(0, 0, 0, 0.8);
  }
}

.im-upload__item-text {
  flex: 1;
}

.im-upload__item-name {
  display: block;
  font-size: 28rpx;
  color: #303133;
  margin-bottom: 8rpx;
  word-break: break-all;
}

.im-upload__item-size {
  display: block;
  font-size: 24rpx;
  color: #909399;
  margin-bottom: 4rpx;
}

.im-upload__item-status-container {
  display: flex;
  align-items: center;
  gap: 16rpx;
}

.im-upload__item-status-text {
  font-size: 24rpx;

  &--pending {
    color: #909399;
  }

  &--uploading {
    color: #409eff;
  }

  &--done {
    color: #52c41a;
  }

  &--error {
    color: #ff4d4f;
  }
}

.im-upload__item-percent-text {
  font-size: 22rpx;
  color: #409eff;
}

.im-upload__item-text-actions {
  display: flex;
  gap: 24rpx;
}

.im-upload__item-text-action {
  font-size: 26rpx;
  color: #409eff;
  cursor: pointer;

  &:active {
    color: #337ecc;
  }

  &--remove {
    color: #ff4d4f;

    &:active {
      color: #d9363e;
    }
  }
}

.im-upload__tip {
  margin-top: 16rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.im-upload__tip-text {
  font-size: 24rpx;
  color: #909399;
}

.im-upload__tip-count {
  font-size: 22rpx;
  color: #409eff;
}

.im-upload__actions {
  margin-top: 24rpx;
  display: flex;
  gap: 20rpx;
  justify-content: flex-end;
}
</style>