<template>
  <div class="material-content" v-loading="loading">
    <div class="flex items-center gap-4">
      <div class="flex items-center">
        <upload
          :params="{ category_id: props.cateId }"
          :type="props.selectTab.type"
          :show-progress="true"
          @allSuccess="handleQuery"
        >
          <el-button type="primary" icon="Upload">本地上传</el-button>
        </upload>
        <template v-if="!props.isPicker">
          <el-button
            class="ml-3"
            :disabled="!selectedItems.length"
            type="danger"
            icon="Delete"
            @click.stop="handleDelete()"
          >
            删除
          </el-button>
          <el-button
            class="ml-3"
            :disabled="!selectedItems.length"
            type="warning"
            icon="Rank"
            @click.stop="openMoveDialog()"
          >
            移动
          </el-button>
        </template>
        <span class="text-sm ml-4" v-else-if="props.limit > 0">
          已选择 {{ selectedItems.length }} / {{ props.limit }}
        </span>
      </div>
      <div class="flex-1 flex justify-end">
        <el-input
          class="max-w-[240px]"
          placeholder="请输入名称"
          v-model="materialParams.keyword"
          @keyup.enter="handleQuery"
        >
          <template #append>
            <el-button @click="handleQuery">
              <template #icon>
                <icon name="Search" />
              </template>
            </el-button>
          </template>
        </el-input>
      </div>
      <el-radio-group v-model="layoutMethod" size="small">
        <el-radio-button label="平铺视图" title="平铺视图" value="normal">
          <icon name="Menu" :size="18" />
        </el-radio-button>
        <el-radio-button label="列表视图" title="列表视图" value="table">
          <icon name="list" :size="18" :type="2" />
        </el-radio-button>
      </el-radio-group>
    </div>
    <div class="mt-3 flex-1 min-h-0">
      <div class="h-full flex flex-col" v-if="layoutMethod === 'normal'">
        <div class="mb-2" v-if="!props.isPicker">
          <el-checkbox
            v-model="isCheckAll"
            :disabled="!lists.length"
            :indeterminate="isIndeterminate"
            @change="handleAllSelect"
          >
            当页全选
          </el-checkbox>
        </div>
        <el-scrollbar class="flex-1" v-if="lists.length">
          <ul class="flex flex-wrap gap-3">
            <li class="material-content__item" :style="{ width: fileSizeUnit }" v-for="item in lists" :key="item.id">
              <material-file :url="item.url" :name="item.name" :file-size="fileSizeUnit" @click="toggleSelect(item)">
                <div class="item-selected" v-if="selectedIds.has(item.id)">
                  <icon :size="32" name="Select" color="#fff" />
                  <div class="selected-index">{{ getItemIndex(item.id) }}</div>
                </div>
              </material-file>
              <div class="w-full mt-1 flex items-center relative">
                <div class="flex-1 text-center truncate text-sm" :title="item.name">{{ item.name }}</div>
                <el-dropdown class="item-dropdown" :hide-on-click="false" @command="handleCommand($event, item)">
                  <div class="pl-3 pr-2 flex items-center">
                    <el-icon :size="14"><MoreFilled /></el-icon>
                  </div>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item command="view">
                        <span v-if="item.type === 'image'">查看图片</span>
                        <span v-else-if="item.type === 'video'">查看视频</span>
                        <span v-else>查看文件</span>
                      </el-dropdown-item>
                      <el-dropdown-item command="copy">复制地址</el-dropdown-item>
                      <el-dropdown-item v-if="!props.isPicker" command="move">
                        <span v-if="item.type === 'image'">移动图片</span>
                        <span v-else-if="item.type === 'video'">移动视频</span>
                        <span v-else>移动文件</span>
                      </el-dropdown-item>
                      <el-dropdown-item v-if="!props.isPicker" command="delete">
                        <span v-if="item.type === 'image'">删除图片</span>
                        <span v-else-if="item.type === 'video'">删除视频</span>
                        <span v-else>删除文件</span>
                      </el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </div>
            </li>
          </ul>
        </el-scrollbar>
        <div class="material-content__empty" v-else-if="!loading">暂无数据~</div>
      </div>
      <el-table
        v-if="layoutMethod === 'table'"
        ref="tableRef"
        :data="lists"
        width="100%"
        height="100%"
        size="large"
        :row-key="(row: any) => row.id"
        @row-click="toggleSelect"
        @selection-change="handleSelectionChange"
      >
        <el-table-column width="55" align="center" v-if="props.isPicker">
          <template #default="{ row }">
            <el-checkbox :modelValue="selectedIds.has(row.id)" @click.stop @change="() => toggleSelect(row)" />
          </template>
        </el-table-column>
        <el-table-column type="selection" width="55" align="center" v-else />
        <el-table-column label="图片" width="100" align="center">
          <template #default="{ row }">
            <div class="flex justify-center items-center">
              <material-file :url="row.url" file-size="40px"></material-file>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="名称" min-width="100" align="center" show-overflow-tooltip>
          <template #default="{ row }">
            <el-link class="duration-300" :underline="false" @click.stop="handlePreview(row.url, row.type)">
              {{ row.name }}
            </el-link>
          </template>
        </el-table-column>
        <el-table-column prop="create_time" label="上传时间" min-width="100" align="center" />
        <el-table-column label="操作" width="160" fixed="right" align="center">
          <template #default="{ row }">
            <el-button type="primary" link @click.stop="handlePreview(row.url, row.type)">查看</el-button>
            <el-button v-if="!props.isPicker" type="warning" link @click.stop="openMoveDialog(row.id, row.category_id)"
              >移动</el-button
            >
            <el-button v-if="!props.isPicker" type="danger" link @click.stop="handleDelete([row.id])">删除</el-button>
            <el-button v-if="props.isPicker" type="warning" link @click.stop="handleCopy(row.url)">复制</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div class="flex justify-center items-center mt-3">
      <pagination
        v-model="materialParams"
        :total="total"
        :pagerCount="5"
        :pageSizes="[10, 20, 30, 50, 100]"
        @change="getLists"
      />
    </div>
    <material-preview v-model="showPreview" :url="previewData.url" :type="previewData.type" />
    <popup v-model="moveDialogVisible" width="380px" @confirm="handleMove" :title="`移动${props.selectTab.name}`">
      <div class="flex items-center gap-3 py-4">
        <span class="truncate text-base">移动至</span>
        <el-tree-select
          class="flex-1"
          v-model="moveCateId"
          :data="props.cateLists"
          placeholder="请选择分组"
          node-key="id"
          :props="{ label: 'name', children: 'children' }"
          check-strictly
        />
      </div>
    </popup>
  </div>
</template>

<script setup lang="ts">
import type { CheckboxValueType } from 'element-plus'
import { materialDelete, materialMove, materialLists } from '@/api/material'
import { addUnit } from '@/utils/util'

// 组件属性
const props = withDefaults(
  defineProps<{
    selectTab: Record<string, any>
    limit: number
    pageLimit: number
    isPicker: boolean
    fileSize: number | string
    cateId: string | number
    cateLists: any[]
    active?: boolean
  }>(),
  {
    active: true
  }
)
// 组件事件
const emit = defineEmits<{
  (e: 'change', selectedItems: any[]): void
}>()
// 表格实例引用
const tableRef = shallowRef()
// 布局模式：normal-平铺视图 | table-表格视图
const layoutMethod = ref<'normal' | 'table'>('normal')
// 列表数据
const loading = ref(false)
const lists = ref<any[]>([])
const total = ref(0)
const materialParams = ref({
  page: 1,
  limit: props.pageLimit,
  keyword: '',
  type: props.selectTab.type,
  category_id: props.cateId
})
// 选择状态
const selectedIds = ref<Set<number>>(new Set())
const selectedItems = ref<any[]>([])
const isCheckAll = ref(false)
const isIndeterminate = ref(false)
const isSyncingSelection = ref(false)
// 弹窗状态
const showPreview = ref(false)
const previewData = reactive<Record<string, any>>({
  url: [],
  type: ''
})
const moveDialogVisible = ref(false)
const moveCateId = ref<number | string>('')
const moveMaterialId = ref<number[]>([])
// 计算属性
const fileSizeUnit = computed(() => addUnit(props.fileSize))
const isSingleSelect = computed(() => props.limit === 1)
const hasLimit = computed(() => props.limit > -1)
const availableSlots = computed(() => (hasLimit.value ? props.limit - selectedItems.value.length : Infinity))
const currentPageSelectedIds = computed(() => new Set(lists.value.map((item) => item.id)))
// 获取选中项的序号
const getItemIndex = (id: number) => {
  const index = Array.from(selectedIds.value).indexOf(id)
  return index >= 0 ? index + 1 : 0
}
// 获取素材列表
const getLists = async () => {
  loading.value = true
  const res = await materialLists(materialParams.value)
  lists.value = res.data
  total.value = res.total
  loading.value = false
  if (selectedItems.value.length > 0) {
    const selectedUrls = new Set(selectedItems.value.map((item) => item.url))
    lists.value.forEach((item) => {
      if (selectedUrls.has(item.url)) {
        selectedIds.value.add(item.id)
      }
    })
    syncTableSelection()
  }
  updateCheckStatus()
}
// 搜索查询
const handleQuery = () => {
  materialParams.value.page = 1
  getLists()
}
// 同步表格选择状态
const syncTableSelection = () => {
  if (layoutMethod.value !== 'table') return
  isSyncingSelection.value = true
  nextTick(() => {
    try {
      tableRef.value?.clearSelection()
      lists.value.forEach((item) => {
        if (selectedIds.value.has(item.id)) {
          tableRef.value?.toggleRowSelection(item, true)
        }
      })
    } finally {
      isSyncingSelection.value = false
    }
  })
}
// 更新全选复选框状态
const updateCheckStatus = () => {
  const currentPageCount = lists.value.filter((item) => selectedIds.value.has(item.id)).length
  isCheckAll.value = currentPageCount === lists.value.length && lists.value.length > 0
  isIndeterminate.value = currentPageCount > 0 && currentPageCount < lists.value.length
}
// 发送选择变化事件
const emitSelectionChange = () => {
  emit('change', selectedItems.value)
}
// 检查选择数量限制
const checkLimit = () => {
  if (hasLimit.value && selectedItems.value.length >= props.limit) {
    ElMessage.warning(`最多只能选择${props.limit}个素材`)
    return true
  }
  return false
}
// 切换选择状态
const toggleSelect = (item: any) => {
  if (selectedIds.value.has(item.id)) {
    selectedIds.value.delete(item.id)
    selectedItems.value = selectedItems.value.filter((i) => i.id !== item.id)
    if (layoutMethod.value === 'table') {
      tableRef.value?.toggleRowSelection(item, false)
    }
  } else {
    if (isSingleSelect.value) {
      selectedIds.value = new Set([item.id])
      selectedItems.value = [item]
      updateCheckStatus()
      emitSelectionChange()
      return
    }
    if (checkLimit()) return
    selectedIds.value.add(item.id)
    selectedItems.value.push(item)
    if (layoutMethod.value === 'table') {
      tableRef.value?.toggleRowSelection(item, true)
    }
  }
  updateCheckStatus()
  emitSelectionChange()
}
// 全选/取消全选
const handleAllSelect = (value: CheckboxValueType) => {
  isIndeterminate.value = false
  if (value) {
    let selectedCount = 0
    const itemsToSelect: any[] = []
    lists.value.forEach((item) => {
      selectedIds.value.delete(item.id)
    })
    for (const item of lists.value) {
      if (selectedCount < availableSlots.value) {
        selectedIds.value.add(item.id)
        if (!selectedItems.value.some((i) => i.id === item.id)) {
          itemsToSelect.push(item)
        }
        selectedCount++
      }
    }
    selectedItems.value = [...selectedItems.value, ...itemsToSelect]
    syncTableSelection()
    if (availableSlots.value < lists.value.length) {
      ElMessage.warning(`最多只能选择${props.limit}个素材`)
    }
  } else {
    lists.value.forEach((item) => {
      selectedIds.value.delete(item.id)
    })
    selectedItems.value = selectedItems.value.filter((item) => !currentPageSelectedIds.value.has(item.id))
    syncTableSelection()
  }
  updateCheckStatus()
  emitSelectionChange()
}
// 表格选择变化
const handleSelectionChange = (selection: any[]) => {
  if (isSyncingSelection.value) return
  isSyncingSelection.value = true
  try {
    const selectedUrls = new Set(selection.map((item) => item.url))
    selectedItems.value = selectedItems.value.filter((item) => {
      if (currentPageSelectedIds.value.has(item.id)) {
        return selectedUrls.has(item.url)
      }
      return true
    })
    selection.forEach((item) => {
      selectedIds.value.add(item.id)
      if (!selectedItems.value.some((i) => i.id === item.id)) {
        selectedItems.value.push(item)
      }
    })
    updateCheckStatus()
    emitSelectionChange()
  } finally {
    isSyncingSelection.value = false
  }
}
// 清空选择
const clearSelect = () => {
  selectedIds.value.clear()
  selectedItems.value = []
  syncTableSelection()
  updateCheckStatus()
  emitSelectionChange()
}
// 预览素材
const handlePreview = (url: string, type: string) => {
  if (type == 'file') {
    window.open(url)
  } else {
    previewData.type = type
    previewData.url = url
    showPreview.value = true
  }
}
// 复制链接
const handleCopy = async (url: string) => {
  try {
    await navigator.clipboard.writeText(url)
    ElMessage.success('地址复制成功')
  } catch {
    ElMessage.error('地址复制失败')
  }
}
// 处理下拉菜单命令
const handleCommand = (command: string, item: any) => {
  switch (command) {
    case 'view':
      handlePreview(item.url, item.type)
      break
    case 'copy':
      handleCopy(item.url)
      break
    case 'move':
      selectedIds.value = new Set([item.id])
      openMoveDialog(item.id, item.category_id)
      break
    case 'delete':
      handleDelete([item.id])
      break
  }
}
// 删除素材
const handleDelete = (ids?: number[]) => {
  ElMessageBox.confirm(`确定要删除所选文件吗？如所选文件已被使用，请谨慎操作！`, '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(async () => {
      const deleteIds = ids || selectedItems.value.map((item) => item.id)
      await materialDelete({ id: deleteIds })
      getLists()
      clearSelect()
    })
    .catch(() => {})
}
// 打开移动分组弹窗
const openMoveDialog = (id?: number, cateId?: number) => {
  if (cateId) {
    moveCateId.value = cateId
  } else if (selectedItems.value.length === 1) {
    moveCateId.value = selectedItems.value[0]?.category_id || ''
  } else if (props.cateLists.length) {
    moveCateId.value = props.cateLists[0]?.id || ''
  } else {
    moveCateId.value = ''
  }
  moveMaterialId.value = id ? [id] : selectedItems.value.map((item) => item.id)
  moveDialogVisible.value = true
}
// 移动素材到分组
const handleMove = async () => {
  await materialMove({ id: moveMaterialId.value, category_id: moveCateId.value })
  moveDialogVisible.value = false
  getLists()
  clearSelect()
}

// 监听分类或类型变化
watch(
  () => [props.cateId, props.selectTab.type],
  () => {
    materialParams.value.category_id = props.cateId
    materialParams.value.type = props.selectTab.type
    materialParams.value.keyword = ''
    handleQuery()
  }
)
// 监听激活状态变化
watch(
  () => props.active,
  (newVal) => {
    if (newVal) {
      handleQuery()
    }
  }
)
// 监听选择状态变化
watch(
  () => [selectedIds.value, layoutMethod.value],
  () => {
    updateCheckStatus()
    syncTableSelection()
    emitSelectionChange()
  },
  { deep: true }
)
// 监听视图切换
watch(layoutMethod, (newVal) => {
  if (newVal === 'table') {
    syncTableSelection()
  }
})

onMounted(() => {
  getLists()
})

defineExpose({
  clearSelect,
  getLists
})
</script>

<style lang="scss" scoped>
.material-content {
  @apply flex-1 flex flex-col min-w-0 min-h-0;
  padding: 16px 16px 12px;

  &__item {
    @apply relative flex flex-col items-center justify-center cursor-pointer;

    .item-selected {
      display: flex;
      align-items: center;
      justify-content: center;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      border-radius: 4px;
      background-color: rgba(0, 0, 0, 0.5);
      box-sizing: border-box;

      .selected-index {
        @apply absolute bottom-0 right-0 bg-primary text-white text-xs px-1.5 rounded-tl-md;
      }
    }

    .item-dropdown {
      opacity: 0;
      visibility: hidden;
      transition: all 0.2s ease;
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      background: linear-gradient(90deg, transparent 0%, var(--el-fill-color-blank) 40%);
      z-index: 10;
    }

    &:hover .item-dropdown {
      opacity: 1;
      visibility: visible;
    }
  }

  &__empty {
    @apply flex flex-1 items-center justify-center text-base;
    color: var(--el-text-color-secondary);
  }
}
</style>
