<template>
  <div class="image-uploader">
    <!-- 持久化隐藏 input，用 key 强制重建，解决移动端相机二次拍摄失效 -->
    <input
      :key="'camera-' + cameraKey"
      ref="cameraInput"
      type="file"
      accept="image/*"
      capture="environment"
      style="display: none"
      @change="onCameraChange"
    />
    <input
      ref="albumInput"
      type="file"
      accept="image/*"
      style="display: none"
      @change="onAlbumChange"
    />

    <div class="upload-grid">
      <div
        v-for="(img, i) in images"
        :key="i"
        class="upload-item"
      >
        <van-image :src="img" width="100%" height="100%" fit="cover" radius="6" />
        <van-icon
          name="cross"
          class="remove-btn"
          @click="removeImage(i)"
        />
      </div>
      <div
        v-if="images.length < max"
        class="upload-item upload-add"
        @click="showAction = true"
      >
        <van-icon name="photograph" size="24" color="#999" />
        <span class="add-text">拍照</span>
      </div>
    </div>

    <van-action-sheet
      v-model:show="showAction"
      :actions="actions"
      cancel-text="取消"
      @select="onSelect"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { showToast } from 'vant'
import { uploadImage } from '@/api/upload'
import { compressImage } from '@/utils/camera'

const props = withDefaults(defineProps<{
  modelValue: string[]
  max?: number
}>(), { max: 9 })

const emit = defineEmits<{ 'update:modelValue': [v: string[]] }>()

const showAction = ref(false)
const uploading = ref(false)
const cameraInput = ref<HTMLInputElement | null>(null)
const albumInput = ref<HTMLInputElement | null>(null)
const cameraKey = ref(0)

const actions = [
  { name: 'camera', description: '拍摄照片' },
  { name: 'album', description: '从相册选择' },
]

const images = computed(() => props.modelValue)

function removeImage(i: number) {
  const arr = [...props.modelValue]
  arr.splice(i, 1)
  emit('update:modelValue', arr)
}

function onSelect(action: { name: string }) {
  showAction.value = false
  if (uploading.value) return

  if (action.name === 'camera') {
    cameraInput.value?.click()
  } else {
    const input = albumInput.value
    if (input) {
      input.multiple = props.max - props.modelValue.length > 1
      input.click()
    }
  }
}

async function onCameraChange(e: Event) {
  const input = e.target as HTMLInputElement
  const files = input.files
  if (!files?.length) {
    input.value = ''
    return
  }
  await uploadFiles(Array.from(files))
  // 移动端相机 input 需要重建 DOM 才能再次触发
  cameraKey.value++
}

async function onAlbumChange(e: Event) {
  const input = e.target as HTMLInputElement
  const files = input.files
  if (!files?.length) {
    input.value = ''
    return
  }
  await uploadFiles(Array.from(files))
  input.value = ''
}

async function uploadFiles(files: File[]) {
  uploading.value = true
  try {
    showToast('上传中...')
    const compressed = await Promise.all(files.map(compressImage))
    const urls = await Promise.all(compressed.map(uploadImage))
    emit('update:modelValue', [...props.modelValue, ...urls])
  } catch (err: any) {
    if (err.message !== '未选择图片') {
      showToast(err.message || '操作失败')
    }
  } finally {
    uploading.value = false
  }
}
</script>

<style scoped>
.upload-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.upload-item {
  position: relative;
  width: calc((100% - 24px) / 4);
  aspect-ratio: 1;
}
.upload-add {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 1px dashed #ddd;
  border-radius: 6px;
  background: #fafafa;
  cursor: pointer;
  gap: 2px;
}
.add-text {
  font-size: 11px;
  color: #999;
}
.remove-btn {
  position: absolute;
  top: -6px;
  right: -6px;
  background: rgba(0,0,0,0.5);
  color: #fff;
  border-radius: 50%;
  padding: 2px;
  font-size: 12px;
}
</style>
