<template>
  <div class="material-picker">
    <div class="material-picker__list" @click.stop v-if="!dialogOnly">
      <div
        class="material-picker__item"
        v-for="(item, index) in materialList"
        :key="index"
        :style="customStyle"
        @click="openPopup(index)"
      >
        <material-file :url="item" :file-size="props.size" />
        <div class="material-picker__item-actions">
          <el-icon :size="20" @click.stop="openPreview(index)"><ZoomIn /></el-icon>
          <el-icon :size="20" @click.stop="removeMaterial(index)"><Delete /></el-icon>
        </div>
      </div>
      <div
        class="material-picker__upload"
        v-show="showUpload"
        :style="customStyle"
        :class="props.uploadClass"
        @click="openPopup(-1)"
      >
        <icon :size="24" name="Plus" />
        <span class="text-base">添加</span>
      </div>
    </div>
    <popup v-model="showDialog" :title="`选择${activeTab.name}`" width="940px" @confirm="handleConfirm">
      <el-scrollbar>
        <div class="min-w-[780px] h-[560px] border-t border-b border-br">
          <material
            :select-tab="activeTab"
            :limit="availableLimit"
            :page-limit="props.pageLimit"
            :is-picker="true"
            :file-size="props.fileSize"
            @change="onMaterialSelect"
          />
        </div>
      </el-scrollbar>
    </popup>
    <material-preview
      v-model="showPreview"
      :url="previewData.url"
      :index="previewData.index"
      :type="previewData.type"
    />
  </div>
</template>

<script setup lang="ts">
import type { CSSProperties } from 'vue'
import { useThrottleFn } from '@vueuse/core'
import { addUnit, getFileType } from '@/utils/util'
import settings from '@/settings'

// 素材类型映射
const tabsList = reactive<Record<string, any>>([
  { type: '', name: '文件' },
  { type: 'image', name: '图片' },
  { type: 'video', name: '视频' }
])
// 组件属性定义
const props = withDefaults(
  defineProps<{
    // 选中的素材列表
    modelValue?: string | string[]
    // 素材类型
    type?: string
    // 选择素材数量限制
    limit?: number
    // 分页数量限制
    pageLimit?: number
    // 上传按钮尺寸
    size?: string | number
    // 上传按钮类名
    uploadClass?: string
    // 上传按钮样式
    uploadStyle?: Record<string, any>
    // 选择弹窗素材尺寸
    fileSize?: string | number
    // 是否仅显示选择弹窗
    dialogOnly?: boolean
  }>(),
  {
    modelValue: '',
    type: 'image',
    limit: -1,
    pageLimit: 30,
    size: 80,
    uploadClass: '',
    uploadStyle: () => ({}),
    fileSize: 100,
    dialogOnly: false
  }
)
// 组件事件定义
const emit = defineEmits<{
  (e: 'update:modelValue', value: string | string[]): void
  (e: 'change', value: any): void
}>()
// 响应式状态
const materialList = ref<string[]>([])
const selectedMaterials = ref<any[]>([])
const showDialog = ref(false)
const showPreview = ref(false)
const previewData = reactive<Record<string, any>>({
  url: [],
  index: 0,
  type: props.type
})
const isEditing = ref(true)
const editIndex = ref(-1)
// 素材项样式
const customStyle = computed<CSSProperties>(() => {
  return {
    ...props.uploadStyle,
    width: addUnit(props.size),
    height: addUnit(props.size)
  }
})
// 当前激活的素材类型标签
const activeTab = computed<Record<string, any>>(() => {
  const foundTab = tabsList.find((tab: Record<string, any>) => tab.type === props.type)
  return foundTab || tabsList[0]
})
// 是否可以添加更多素材
const showUpload = computed(() => {
  return props.limit === -1 || materialList.value.length < props.limit
})
// 可用的选择限制
const availableLimit = computed(() => {
  if (!isEditing.value) {
    return 1
  }
  if (props.limit === -1) {
    return undefined
  }
  return props.limit - materialList.value.length
})
// 初始化素材列表
const initMaterialList = (value: string | string[]): void => {
  materialList.value = Array.isArray(value) ? value : value ? [value] : []
}
// 打开素材选择器
const openPopup = (index: number): void => {
  if (index >= 0) {
    isEditing.value = false
    editIndex.value = index
  } else {
    isEditing.value = true
    editIndex.value = -1
  }
  selectedMaterials.value = []
  showDialog.value = true
}
// 处理素材选择变化
const onMaterialSelect = (materials: any[]): void => {
  selectedMaterials.value = materials
}
// 确认选择
const handleConfirm = useThrottleFn(
  () => {
    const selectedUrls = selectedMaterials.value.map((item) => item.url)
    if (!isEditing.value && selectedUrls.length > 0) {
      // 编辑模式：替换当前项
      materialList.value.splice(editIndex.value, 1, selectedUrls[0])
    } else if (selectedUrls.length > 0) {
      // 添加模式：合并选择结果，去重
      const existingSet = new Set(materialList.value)
      selectedUrls.forEach((url) => {
        if (!existingSet.has(url)) {
          existingSet.add(url)
        }
      })
      materialList.value = Array.from(existingSet)
    }
    showDialog.value = false
    emitSelectionChange()
  },
  1000,
  false
)
// 打开预览
const openPreview = (index: number): void => {
  if (props.type == 'image') {
    previewData.url = materialList.value
    previewData.index = index
  } else {
    const ext = getFileType(previewData.url)
    if (settings.imageType.includes(`.${ext}`)) {
      previewData.type = 'image'
      previewData.index = 0
    } else if (settings.videoType.includes(`.${ext}`)) {
      previewData.type = 'video'
    } else {
      window.open(previewData.url)
      return
    }
  }
  previewData.type = props.type
  previewData.url = materialList.value[index]
  showPreview.value = true
}
// 移除素材
const removeMaterial = (index: number): void => {
  materialList.value.splice(index, 1)
  emitSelectionChange()
}
// 触发选择变化事件
const emitSelectionChange = (): void => {
  const result = props.limit !== 1 ? materialList.value : materialList.value[0] || ''
  emit('update:modelValue', result)
  emit('change', result)
}

watch(
  () => props.modelValue,
  (newVal) => {
    initMaterialList(newVal)
  },
  { immediate: true }
)
watch(
  () => props.type,
  () => {
    initMaterialList(props.modelValue)
  }
)
watch(showDialog, (newVal) => {
  if (!newVal) {
    selectedMaterials.value = []
  }
})

defineExpose({
  open: openPopup,
  openPreview
})
</script>

<style lang="scss" scoped>
.material-picker {
  &__list {
    @apply flex flex-wrap gap-2 items-center;
  }

  &__item {
    @apply relative overflow-hidden cursor-pointer;
    border-radius: 4px;

    &-actions {
      @apply flex justify-center items-center gap-2;
      opacity: 0;
      transition: opacity 0.3s ease-in-out;
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
      color: #fff;
      background-color: rgba(0, 0, 0, 0.4);
    }

    &:hover .material-picker__item-actions {
      opacity: 1;
    }
  }

  &__upload {
    @apply rounded border-br border-dashed border-2;
    @apply cursor-pointer text-tx-secondary box-border transition-all duration-300;
    @apply flex flex-col justify-center items-center;

    &:hover {
      @apply border-primary text-primary;
    }
  }
}
</style>
