<template>
  <view class="file-picker-wrapper">
    <!-- 只读模式：仅展示 -->
    <view v-if="readonly" class="file-list readonly-list">
      <view v-for="(file, index) in innerFileList" :key="index" class="file-item readonly-item"
        @click="previewFile(file, index)">
        <image v-if="isImage(file)" :src="file.url || file.path" mode="aspectFill" class="file-thumb" />
        <view v-else class="file-icon" :class="getFileClass(file.name)">
          <text class="file-icon-text">{{ getFileIcon(file.name) }}</text>
        </view>
        <text class="file-name">{{ file.name }}</text>
      </view>
      <view v-if="!innerFileList.length" class="empty-text">暂无文件</view>
    </view>

    <!-- 正常模式：宫格上传（完全对齐官方 upload-image 样式） -->
    <view v-else class="uni-file-picker__container">
      <view class="file-picker__box" v-for="(item, index) in innerFileList" :key="index" :style="boxStyle">
        <view class="file-picker__box-content" :style="borderStyle">
          <!-- 图片 -->
          <image v-if="isImage(item)" class="file-image" :src="item.url || item.path" mode="aspectFill"
            @click.stop="previewFile(item, index)" />

          <!-- 视频 → 显示第一帧 + 播放图标 -->
          <view v-else-if="isVideo(item)" class="file-cover video-box" @click.stop="previewFile(item, index)">
            <image :src="item.url || item.path" class="file-image" mode="aspectFill" />
            <view class="video-play-icon">▶</view>
          </view>

          <!-- 其他文件（PDF/Word/Excel） -->
          <view v-else class="file-cover file-box" :class="getFileClass(item.name)"
            @click.stop="previewFile(item, index)">
            <text class="file-big-icon">{{ getFileIcon(item.name) }}</text>
            <text class="file-ext-name">{{ getFileExt(item.name) }}</text>
          </view>

          <!-- 删除 -->
          <view v-if="delIcon" class="icon-del-box" @click.stop="deleteFile(index)">
            <view class="icon-del"></view>
            <view class="icon-del rotate"></view>
          </view>

          <!-- 进度 -->
          <view v-if="item.status === 'uploading'" class="file-picker__progress">
            <progress class="file-picker__progress-item" :percent="item.progress" stroke-width="4"
              backgroundColor="#EBEBEB" />
          </view>

          <!-- 失败重试 -->
          <view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="reUploadFile(index)">
            点击重试
          </view>
        </view>
      </view>

      <!-- 添加按钮 -->
      <view v-if="innerFileList.length < limit" class="file-picker__box" :style="boxStyle">
        <view class="file-picker__box-content is-add" :style="borderStyle" @click="handleChoose">
          <cwg-icon name="icon_add" class="upload-icon" :size="24" />
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, watch, nextTick } from 'vue'
import config from '@/config'
import { userToken } from '@/composables/config'

// === Vue3 v-model 标准写法 + 多类型兼容 ===
const props = defineProps({
  modelValue: {
    type: [Array, String, Object],
    default: () => []
  },
  readonly: {
    type: Boolean,
    default: false
  },
  disabled: {
    type: Boolean,
    default: false
  },
  limit: {
    type: Number,
    default: 9
  },
  fileMediatype: {
    type: String,
    default: 'all'
  },
  delIcon: {
    type: Boolean,
    default: true
  },
  disablePreview: {
    type: Boolean,
    default: false
  },
  autoUpload: {
    type: Boolean,
    default: true
  },
  action: {
    type: String,
    default: ''
  },
  uploadUrl: {
    type: String,
    default: '/custom/bank/upload'
  },
  uploadHeaders: {
    type: Object,
    default: () => ({})
  },
  uploadName: {
    type: String,
    default: 'file'
  },
  uploadData: {
    type: Object,
    default: () => ({})
  },
  responseHandler: {
    type: Function,
    default: (res) => {
      try {
        const data = typeof res === 'string' ? JSON.parse(res) : res
        return {
          success: data.code === 200,
          path: data.data?.path || data.data,
          message: data.msg || '上传成功'
        }
      } catch (e) {
        return { success: false, message: '解析失败' }
      }
    }
  }
})

const emit = defineEmits([
  'update:modelValue',
  'change',
  'delete',
  'success',
  'fail',
  'progress',
  'select'
])

// 内部数据
const innerFileList = ref([])
const tempFileQueue = ref([])
const originalType = ref('array') // 'string' | 'object' | 'array'

const boxStyle = 'width:33.3%;height:0;padding-top:33.3%;'
const borderStyle = 'border:1px #eee solid;border-radius:5px;'

// ==============================================
// 统一格式化函数（修复递归核心）
// ==============================================
function formatValue(val) {
  if (!val) return []

  // 字符串
  if (typeof val === 'string') {
    return [{
      path: val,
      url: val.startsWith('http') ? val : config.Host05 + val,
      name: val.split('/').pop(),
      status: 'success'
    }]
  }

  // 对象
  if (typeof val === 'object' && !Array.isArray(val)) {
    const path = val.path || val.url || ''
    return [{
      ...val,
      path,
      url: val.url || (path.startsWith('http') ? path : config.Host05 + path),
      name: val.name || path.split('/').pop(),
      status: 'success'
    }]
  }

  // 数组
  if (Array.isArray(val)) {
    return val.map(item => {
      if (typeof item === 'string') {
        return {
          path: item,
          url: item.startsWith('http') ? item : config.Host05 + item,
          name: item.split('/').pop(),
          status: 'success'
        }
      } else {
        const path = item?.path || item?.url || ''
        return {
          ...item,
          path,
          url: item?.url || (path.startsWith('http') ? path : config.Host05 + path),
          name: item?.name || path.split('/').pop(),
          status: 'success'
        }
      }
    })
  }
  return []
}

// ==============================================
// 监听外部传入：防重复赋值 → 无递归
// ==============================================
watch(
  () => props.modelValue,
  (val) => {
    const formatted = formatValue(val)
    // 值相同不更新，防止死循环
    if (JSON.stringify(innerFileList.value) === JSON.stringify(formatted)) return

    if (!val) {
      innerFileList.value = []
      originalType.value = 'array'
      return
    }

    // 记录原始类型
    if (typeof val === 'string') originalType.value = 'string'
    else if (typeof val === 'object' && !Array.isArray(val)) originalType.value = 'object'
    else originalType.value = 'array'

    innerFileList.value = formatted
  },
  { immediate: true, deep: true }
)

// ==============================================
// 内部变化同步外部：nextTick → 无递归
// ==============================================
watch(
  innerFileList,
  (list) => {
    nextTick(() => {
      let returnValue = []

      if (!list || list.length === 0) {
        returnValue = originalType.value === 'string' ? '' :
          originalType.value === 'object' ? {} : []
        emitUpdate(returnValue)
        return
      }

      // 只返回干净的 path 数据
      const cleanList = list.map(item => item.path || item.url || '')
      console.log(originalType);

      // 按原始类型返回
      if (props.limit === 1) {
        returnValue = cleanList[0] || ''
      } else if (originalType.value === 'string') returnValue = cleanList[0] || ''
      else if (originalType.value === 'object') returnValue = { path: cleanList[0] || '' }
      else returnValue = cleanList

      emitUpdate(returnValue)
    })
  },
  { deep: true }
)

// 统一触发更新
function emitUpdate(val) {
  emit('update:modelValue', val)
  emit('change', val)
}

// ==============================================
// 文件类型判断
// ==============================================
const isImage = (file) => {
  const ext = (file.path || file.name || '').split('.').pop()?.toLowerCase()
  return ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg'].includes(ext)
}

const isVideo = (file) => {
  const ext = (file.path || file.name || '').split('.').pop()?.toLowerCase()
  return ['mp4', 'mov', 'avi', 'flv', 'wmv', 'rmvb'].includes(ext)
}

const getFileExt = (name) => {
  if (!name) return ''
  return name.split('.').pop()?.toUpperCase()
}

const getFileIcon = (name) => {
  const ext = getFileExt(name)
  if (ext === 'PDF') return 'PDF'
  if (['DOC', 'DOCX'].includes(ext)) return 'WORD'
  if (['XLS', 'XLSX'].includes(ext)) return 'EXCEL'
  if (isVideo({ name })) return '▶'
  if (isImage({ name })) return 'IMG'
  return 'FILE'
}

const getFileClass = (name) => {
  const ext = getFileExt(name)
  if (ext === 'PDF') return 'file-pdf'
  if (['DOC', 'DOCX'].includes(ext)) return 'file-word'
  if (['XLS', 'XLSX'].includes(ext)) return 'file-excel'
  if (isVideo({ name })) return 'file-video'
  return 'file-other'
}

// ==============================================
// 预览
// ==============================================
const previewFile = (file, index) => {
  if (props.disablePreview || file.status === 'uploading' || file.status === 'error') return
  const url = file.url || file.path
  const name = file.name || '文件'

  if (isImage(file)) {
    const successImages = innerFileList.value.filter(item => isImage(item) && item.status === 'success')
    const realIndex = successImages.findIndex(item => item.url === file.url && item.path === file.path)
    const urls = successImages.map(item => item.url || item.path)
    uni.previewImage({ current: realIndex, urls, loop: true })
    return
  }

  uni.navigateTo({
    url: `/pages/common/webview?url=${encodeURIComponent(url)}&title=${encodeURIComponent(name)}`
  })
}

// ==============================================
// 上传 / 删除 / 重试
// ==============================================
const deleteFile = (index) => {
  const item = innerFileList.value[index]
  innerFileList.value.splice(index, 1)
  emit('delete', item, index)
}

const handleChoose = () => {
  if (props.disabled || props.readonly) return
  const count = props.limit - innerFileList.value.length
  uni.chooseFile({
    type: props.fileMediatype,
    count,
    success: (res) => {
      const files = res.tempFiles || res.tempFilePaths.map((p, i) => ({
        path: p, name: `file_${i}`
      }))
      emit('select', { tempFiles: files })
      tempFileQueue.value = files
      if (props.autoUpload) startUpload()
    }
  })
}

const startUpload = async () => {
  const files = tempFileQueue.value
  tempFileQueue.value = []
  for (const file of files) await uploadFile(file)
}

const uploadFile = (fileItem) => {
  return new Promise((resolve) => {
    innerFileList.value.push({ ...fileItem, status: 'uploading', progress: 0 })
    const index = innerFileList.value.length - 1
    const url = props.action || config.Host80 + props.uploadUrl

    const task = uni.uploadFile({
      url,
      filePath: fileItem.path,
      name: props.uploadName,
      header: { 'Access-Token': userToken.value, ...props.uploadHeaders },
      formData: props.uploadData,

      success: (res) => {
        const result = props.responseHandler(res.data)
        if (result.success) {
          innerFileList.value[index].progress = 100
          innerFileList.value[index].status = 'success'
          innerFileList.value[index].url = config.Host05 + result.path
          innerFileList.value[index].path = result.path
          emit('success', innerFileList.value[index])
        } else {
          innerFileList.value[index].status = 'error'
          uni.showToast({ title: result.message, icon: 'error' })
          emit('fail', result.message)
        }
        resolve(result)
      },

      fail: () => {
        innerFileList.value[index].status = 'error'
        uni.showToast({ title: '上传失败', icon: 'error' })
        emit('fail', '网络异常')
        resolve(null)
      }
    })

    task.onProgressUpdate((p) => {
      innerFileList.value[index].progress = p.progress
      emit('progress', p, index)
    })
  })
}

const reUploadFile = (index) => {
  const file = innerFileList.value[index]
  uploadFile(file)
}
</script>

<style scoped>
/* 布局 */
.uni-file-picker__container {
  display: flex;
  flex-wrap: wrap;
  margin: -5px;
  width: 100%;
}

.file-picker__box {
  position: relative;
  width: 33.3%;
  height: 0;
  padding-top: 33.3%;
  box-sizing: border-box;
}

.file-picker__box-content {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: 5px;
  border-radius: 5px;
  overflow: hidden;
  background: #f7f7f7;
}

/* 图片 */
.file-image {
  width: 100%;
  height: 100%;
}

/* 文件封面 */
.file-cover {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  color: #fff;
}

/* 视频 */
.video-box {
  position: relative;
}

.video-play-icon {
  position: absolute;
  font-size: 30px;
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 文件类型颜色 */
.file-pdf {
  background: #ff4f4f;
}

.file-word {
  background: #3a7ff5;
}

.file-excel {
  background: #24a148;
}

.file-video {
  background: #000;
}

.file-other {
  background: #f7f7f7;
}

.file-big-icon {
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 4px;
}

.file-ext-name {
  font-size: 12px;
  opacity: 0.9;
}

/* 添加按钮 */
.is-add {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f7f7f7;
}

.upload-icon {
  color: #999;
}

/* 删除按钮 */
.icon-del-box {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  transform: rotate(-45deg);
}

.icon-del {
  width: 15px;
  height: 2px;
  background: #fff;
  border-radius: 2px;
}

.rotate {
  position: absolute;
  transform: rotate(90deg);
}

/* 进度 */
.file-picker__progress {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
}

/* 失败遮罩 */
.file-picker__mask {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.4);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}

/* 只读模式 */
.file-list {
  padding: 5px;
}

.file-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  background: #f8f8f8;
  border-radius: 8px;
  margin-bottom: 10px;
}

.file-thumb,
.file-icon {
  width: 60px;
  height: 60px;
  border-radius: 6px;
}

.file-icon {
  background: #eee;
  display: flex;
  align-items: center;
  justify-content: center;
}

.file-icon-text {
  font-size: 12px;
  font-weight: bold;
}

.file-name {
  flex: 1;
  font-size: 14px;
  color: #333;
}

.empty-text {
  text-align: center;
  color: #999;
  padding: 20px 0;
}
</style>