<script setup lang="ts">
usePageTitle('图片孤儿审查')

type OrphanItem = {
  id: number
  storageKey: string
  publicUrl: string
  sizeBytes: number
  createdAt: string
  firstReferencedAt: string | null
  dereferencedAt: string | null
  /** 宽限结束、允许删除的绝对时间（ISO）；无法推算时为 null */
  graceExpiresAt: string | null
  state: 'deletable' | 'cooling'
}

type Filter = 'all' | 'deletable' | 'cooling'

const toast = useToast()
const { fetchData } = useClientApi()

const filter = ref<Filter>('all')
const page = ref(1)
const pageSize = ref(20)
const items = ref<OrphanItem[]>([])
const total = ref(0)
const loading = ref(true)
const selectedIds = ref<Set<number>>(new Set())
const confirmOpen = ref(false)
const confirmIds = ref<number[]>([])
const deleting = ref(false)

const filterItems = [
  { label: '全部', value: 'all' as const },
  { label: '可删除', value: 'deletable' as const },
  { label: '宽限期', value: 'cooling' as const },
]

const pageSizeItems = [
  { label: '每页 10', value: 10 },
  { label: '每页 20', value: 20 },
  { label: '每页 50', value: 50 },
]

const deletableOnPage = computed(() => items.value.filter((i) => i.state === 'deletable'))

const allDeletableOnPageSelected = computed(() => {
  const d = deletableOnPage.value
  return d.length > 0 && d.every((i) => selectedIds.value.has(i.id))
})

const confirmDescription = computed(() => {
  const n = confirmIds.value.length
  return n === 1 ? '确定删除该图片？此操作不可恢复。' : `确定删除选中的 ${n} 张图片？此操作不可恢复。`
})

function formatBytes(n: number): string {
  if (n < 1024) {
    return `${n} B`
  }
  if (n < 1024 * 1024) {
    return `${(n / 1024).toFixed(1)} KB`
  }
  return `${(n / (1024 * 1024)).toFixed(1)} MB`
}

function formatDt(iso: string | null): string {
  if (!iso) {
    return '—'
  }
  const d = new Date(iso)
  return Number.isNaN(d.getTime()) ? iso : d.toLocaleString('zh-CN')
}

async function load() {
  loading.value = true
  try {
    const q = new URLSearchParams({
      filter: filter.value,
      page: String(page.value),
      pageSize: String(pageSize.value),
    })
    const body = await fetchData<{ items: OrphanItem[]; total: number }>(
      `/api/me/media/orphans?${q.toString()}`,
    )
    items.value = body.items
    total.value = body.total
    const allowed = new Set(body.items.filter((i) => i.state === 'deletable').map((i) => i.id))
    selectedIds.value = new Set([...selectedIds.value].filter((id) => allowed.has(id)))
  } finally {
    loading.value = false
  }
}

watch([filter, pageSize], () => {
  page.value = 1
})

watch(
  [filter, page, pageSize],
  () => {
    void load()
  },
  { immediate: true },
)

function toggleSelected(id: number, checked: boolean) {
  const next = new Set(selectedIds.value)
  if (checked) {
    next.add(id)
  } else {
    next.delete(id)
  }
  selectedIds.value = next
}

function toggleSelectAllOnPage(checked: boolean) {
  if (!checked) {
    const next = new Set(selectedIds.value)
    for (const i of deletableOnPage.value) {
      next.delete(i.id)
    }
    selectedIds.value = next
    return
  }
  const next = new Set(selectedIds.value)
  for (const i of deletableOnPage.value) {
    next.add(i.id)
  }
  selectedIds.value = next
}

const batchCount = computed(() => [...selectedIds.value].length)

function openConfirm(ids: number[]) {
  confirmIds.value = ids
  confirmOpen.value = true
}

async function executeDelete() {
  deleting.value = true
  try {
    const { deleted } = await fetchData<{ deleted: number }>('/api/me/media/orphans-delete', {
      method: 'POST',
      body: { ids: confirmIds.value },
    })
    toast.add({ title: `已删除 ${deleted} 项`, color: 'success' })
    confirmOpen.value = false
    selectedIds.value = new Set()
    await load()
  } finally {
    deleting.value = false
  }
}
</script>

<template>
  <div class="space-y-6 w-full max-w-[min(100%,88rem)]">
    <div class="flex flex-wrap justify-between items-start gap-3">
      <div>
        <h2 class="text-lg font-semibold">
          图片孤儿审查
        </h2>
        <p class="text-sm text-muted mt-1">
          未被任何文章引用的上传图片；宽限期结束后可删除。
        </p>
      </div>
      <UButton to="/me" variant="ghost" size="sm">
        返回控制台
      </UButton>
    </div>

    <div class="flex flex-wrap items-end gap-x-5 gap-y-3">
      <UFormField label="筛选" class="w-44 shrink-0">
        <USelect v-model="filter" :items="filterItems" value-key="value" class="w-full" />
      </UFormField>
      <UFormField label="每页" class="w-40 shrink-0">
        <USelect v-model="pageSize" :items="pageSizeItems" value-key="value" class="w-full" />
      </UFormField>
      <div class="shrink-0">
        <UButton
          :color="batchCount > 0 ? 'error' : 'neutral'"
          variant="outline"
          :disabled="batchCount === 0"
          class="min-h-8"
          @click="openConfirm([...selectedIds])"
        >
          批量删除（{{ batchCount }}）
        </UButton>
      </div>
    </div>

    <div v-if="loading" class="text-muted">
      加载中…
    </div>
    <UEmpty v-else-if="!items.length" title="暂无记录" description="当前筛选下没有孤儿图片" />
    <div v-else class="overflow-x-auto rounded-lg border border-default">
      <table class="w-full min-w-[960px] table-fixed text-sm">
        <thead>
          <tr class="text-left text-muted border-b border-default bg-elevated/40">
            <th class="p-3 w-10 align-middle">
              <UCheckbox
                v-if="deletableOnPage.length"
                :model-value="allDeletableOnPageSelected"
                @update:model-value="toggleSelectAllOnPage(!!$event)"
              />
            </th>
            <th class="p-3 w-20 align-middle">
              预览
            </th>
            <th class="p-3 align-middle w-[32%]">
              文件
            </th>
            <th class="p-3 whitespace-nowrap w-16 align-middle">
              大小
            </th>
            <th class="p-3 whitespace-nowrap w-36 align-middle">
              创建
            </th>
            <th class="p-3 whitespace-nowrap hidden lg:table-cell w-36 align-middle">
              首次引用
            </th>
            <th class="p-3 whitespace-nowrap hidden lg:table-cell w-36 align-middle">
              解除引用
            </th>
            <th class="p-3 whitespace-nowrap w-40 align-middle">
              到期可删
            </th>
            <th class="p-3 w-24 align-middle whitespace-nowrap">
              状态
            </th>
            <th class="p-3 text-right w-24 align-middle whitespace-nowrap">
              操作
            </th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="row in items" :key="row.id" class="border-b border-default/60 last:border-b-0">
            <td class="p-3 align-middle">
              <UCheckbox
                v-if="row.state === 'deletable'"
                :model-value="selectedIds.has(row.id)"
                @update:model-value="toggleSelected(row.id, !!$event)"
              />
              <span v-else class="text-muted text-xs">—</span>
            </td>
            <td class="p-3 align-middle">
              <img
                :src="row.publicUrl"
                :alt="row.storageKey"
                class="h-14 w-14 shrink-0 object-cover rounded border border-default"
                loading="lazy"
              >
            </td>
            <td class="p-3 align-middle max-w-0">
              <div
                class="truncate font-mono text-xs text-default"
                :title="row.storageKey"
              >
                {{ row.storageKey }}
              </div>
            </td>
            <td class="p-3 align-middle tabular-nums whitespace-nowrap">
              {{ formatBytes(row.sizeBytes) }}
            </td>
            <td class="p-3 align-middle text-xs whitespace-nowrap">
              {{ formatDt(row.createdAt) }}
            </td>
            <td class="p-3 align-middle text-xs whitespace-nowrap hidden lg:table-cell">
              {{ formatDt(row.firstReferencedAt) }}
            </td>
            <td class="p-3 align-middle text-xs whitespace-nowrap hidden lg:table-cell">
              {{ formatDt(row.dereferencedAt) }}
            </td>
            <td class="p-3 align-middle text-xs whitespace-nowrap tabular-nums">
              <span
                :class="row.state === 'deletable' ? 'text-muted' : 'text-default'"
                :title="row.graceExpiresAt ? `到达该时间后即可删除（或已可删）` : ''"
              >
                {{ formatDt(row.graceExpiresAt) }}
              </span>
            </td>
            <td class="p-3 align-middle">
              <div class="flex items-center justify-start">
                <UBadge
                  v-if="row.state === 'deletable'"
                  color="error"
                  variant="subtle"
                  size="sm"
                  class="whitespace-nowrap"
                >
                  可删除
                </UBadge>
                <UBadge
                  v-else
                  color="warning"
                  variant="subtle"
                  size="sm"
                  class="whitespace-nowrap"
                >
                  宽限期
                </UBadge>
              </div>
            </td>
            <td class="p-3 align-middle text-right">
              <UButton
                v-if="row.state === 'deletable'"
                size="xs"
                color="error"
                variant="soft"
                class="whitespace-nowrap"
                @click="openConfirm([row.id])"
              >
                删除
              </UButton>
              <span
                v-else
                class="inline-block text-left text-xs text-muted leading-snug max-w-[5.5rem]"
                title="宽限期未满，不可删除。可在「可删除」筛选中查看已到期项。"
              >
                宽限中
              </span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <div v-if="!loading && total > pageSize" class="flex justify-end">
      <UPagination v-model:page="page" :total="total" :items-per-page="pageSize" size="sm" />
    </div>

    <UModal v-model:open="confirmOpen" title="确认删除" :description="confirmDescription">
      <template #footer>
        <div class="flex justify-end gap-2">
          <UButton variant="outline" color="neutral" :disabled="deleting" @click="confirmOpen = false">
            取消
          </UButton>
          <UButton color="error" :loading="deleting" @click="executeDelete">
            删除
          </UButton>
        </div>
      </template>
    </UModal>
  </div>
</template>
