<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { NAlert, NButton, NCard, NDropdown, NEmpty, NIcon, NSkeleton, NTag, useMessage } from 'naive-ui'
import EncyclopediaProgressCard from '@/components/cloud/EncyclopediaProgressCard.vue'
import { Settings } from '@vicons/tabler'
import CloudEditModal, { type CloudEditFormValue } from '@/components/cloud/CloudEditModal.vue'
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
import MiniLocationMap from '@/components/cloud/MiniLocationMap.vue'
import ContributionHeatmap from '@/components/profile/ContributionHeatmap.vue'
import { useAuthStore } from '@/stores/auth'
import { useCloudsStore } from '@/stores/clouds'
import { useEncyclopediaStore } from '@/stores/encyclopedia'
import { useProfileStore, type ProfileCloudItem } from '@/stores/profile'
import type { CloudType, Profile } from '@/types/database'

interface TimelineGroup {
  key: string
  label: string
  items: ProfileCloudItem[]
}

const route = useRoute()
const authStore = useAuthStore()
const cloudsStore = useCloudsStore()
const encyclopediaStore = useEncyclopediaStore()
const profileStore = useProfileStore()
const message = useMessage()

const selectedCloud = ref<ProfileCloudItem | null>(null)
const selectedUploadDate = ref<string | null>(null)
const timelineSectionRef = ref<HTMLElement | null>(null)
const selectionMode = ref(false)
const selectedCloudIds = ref<Set<string>>(new Set())
const editModalOpen = ref(false)
const editSaving = ref(false)
const manageError = ref('')
const editInitialValue = ref<CloudEditFormValue | null>(null)

const rarityMeta = {
  common: { label: '常见', chip: 'bg-sky-100 text-sky-700 border-sky-200' },
  uncommon: { label: '少见', chip: 'bg-amber-100 text-amber-700 border-amber-200' },
  rare: { label: '罕见', chip: 'bg-rose-100 text-rose-700 border-rose-200' },
} satisfies Record<CloudType['rarity'], { label: string; chip: string }>

const statusMeta = {
  approved: { label: '已通过', chip: 'bg-emerald-100 text-emerald-700 border-emerald-200' },
  pending: { label: '待审核', chip: 'bg-amber-100 text-amber-700 border-amber-200' },
  rejected: { label: '未通过', chip: 'bg-rose-100 text-rose-700 border-rose-200' },
} satisfies Record<ProfileCloudItem['status'], { label: string; chip: string }>

const viewedUserId = computed(() => {
  const routeUserId = typeof route.params.id === 'string' ? route.params.id : null
  return routeUserId || authStore.user?.id || null
})

const isOwnProfile = computed(() => {
  return !!viewedUserId.value && viewedUserId.value === authStore.user?.id
})

const loading = computed(() => profileStore.isLoading(viewedUserId.value, isOwnProfile.value))
const errorMsg = computed(() => {
  if (!viewedUserId.value) return '未找到要查看的用户。'
  return profileStore.getError(viewedUserId.value, isOwnProfile.value)
})
const profileData = computed<Profile | null>(() => profileStore.getProfile(viewedUserId.value))
const clouds = computed<ProfileCloudItem[]>(() => profileStore.getClouds(viewedUserId.value, isOwnProfile.value))

const pageTitle = computed(() => {
  return isOwnProfile.value ? '我的天空日志' : `${profileData.value?.username || '这位用户'}的天空日志`
})

const profileSubtitle = computed(() => {
  if (isOwnProfile.value) {
    return '回看你的云图记录、收集进度和拍摄节奏。'
  }
  return '公开展示的云图记录会按时间顺序陈列在这里。'
})

const totalShots = computed(() => clouds.value.length)

const approvedShots = computed(() => {
  return clouds.value.filter(item => item.status === 'approved').length
})

const pendingShots = computed(() => {
  return clouds.value.filter(item => item.status === 'pending').length
})

const selectedCloudCount = computed(() => selectedCloudIds.value.size)

const cloudActionOptions = computed(() => {
  if (!selectedCloud.value) return []

  return [
    { label: '编辑', key: 'edit' },
    { label: '删除', key: 'delete' },
    {
      label: selectedCloud.value.is_hidden ? '更改为公开' : '更改为私密',
      key: 'toggle-privacy',
    },
  ]
})

const shootingDays = computed(() => {
  return new Set(
    clouds.value.map(item => toDayKey(item.captured_at || item.created_at)),
  ).size
})

const uploadDayCounts = computed(() => {
  const counts = new Map<string, number>()

  for (const item of clouds.value) {
    const key = toDayKey(item.created_at)
    counts.set(key, (counts.get(key) || 0) + 1)
  }

  return counts
})

const mostCommonCloudType = computed(() => {
  const counts = new Map<string, number>()
  for (const item of clouds.value) {
    counts.set(item.cloudTypeName, (counts.get(item.cloudTypeName) || 0) + 1)
  }

  let winner = '还没有记录'
  let maxCount = 0
  for (const [name, count] of counts.entries()) {
    if (count > maxCount) {
      winner = name
      maxCount = count
    }
  }

  return {
    name: winner,
    count: maxCount,
  }
})

const filteredClouds = computed(() => {
  if (!selectedUploadDate.value) return clouds.value

  return clouds.value.filter(item => toDayKey(item.created_at) === selectedUploadDate.value)
})

const timelineGroups = computed<TimelineGroup[]>(() => {
  const groups = new Map<string, TimelineGroup>()

  for (const item of filteredClouds.value) {
    const date = new Date(item.captured_at || item.created_at)
    const key = `${date.getFullYear()}-${date.getMonth() + 1}`
    const existing = groups.get(key)

    if (existing) {
      existing.items.push(item)
      continue
    }

    groups.set(key, {
      key,
      label: `${date.getFullYear()}年${date.getMonth() + 1}月`,
      items: [item],
    })
  }

  return Array.from(groups.values())
})

const selectedUploadCount = computed(() => {
  if (!selectedUploadDate.value) return 0
  return uploadDayCounts.value.get(selectedUploadDate.value) || 0
})

const contributionCells = computed(() => {
  return Array.from(uploadDayCounts.value.entries())
    .map(([date, count]) => ({ date, count }))
    .sort((a, b) => a.date.localeCompare(b.date))
})

const activeUploadDays = computed(() => {
  return contributionCells.value.filter(cell => cell.count > 0).length
})

const currentUploadStreak = computed(() => {
  let streak = 0
  const cursor = startOfDay(new Date())

  while (true) {
    const key = toLocalDayKey(cursor)
    if ((uploadDayCounts.value.get(key) || 0) <= 0) break
    streak += 1
    cursor.setDate(cursor.getDate() - 1)
  }

  return streak
})

const longestUploadStreak = computed(() => {
  let best = 0
  let current = 0
  let previousDate: Date | null = null

  for (const cell of contributionCells.value) {
    if (cell.count <= 0) continue

    const currentDate = startOfDay(new Date(cell.date))

    if (!previousDate) {
      current = 1
    } else {
      const diffDays = Math.round((currentDate.getTime() - previousDate.getTime()) / 86400000)
      current = diffDays === 1 ? current + 1 : 1
    }

    if (current > best) best = current
    previousDate = currentDate
  }

  return best
})

function toDayKey(iso: string) {
  const date = new Date(iso)
  return toLocalDayKey(date)
}

function toLocalDayKey(date: Date) {
  const month = `${date.getMonth() + 1}`.padStart(2, '0')
  const day = `${date.getDate()}`.padStart(2, '0')
  return `${date.getFullYear()}-${month}-${day}`
}

function startOfDay(date: Date) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}

function formatDate(iso: string | null) {
  if (!iso) return '未知时间'
  return new Date(iso).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  })
}

function formatDayKey(dayKey: string | null) {
  if (!dayKey) return ''
  const [year, month, day] = dayKey.split('-').map(Number)
  return new Date(year, (month || 1) - 1, day || 1).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  })
}

function formatDateTime(iso: string | null) {
  if (!iso) return '未知时间'
  return new Date(iso).toLocaleString('zh-CN', {
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  })
}

function formatCoordinate(value: number | null) {
  return value === null ? '未记录' : value.toFixed(2)
}

function formatCloudTime(item: ProfileCloudItem) {
  return formatDateTime(item.captured_at || item.created_at)
}

function getProfileInitial(profile: Profile | null) {
  return profile?.username?.slice(0, 1).toUpperCase() || '云'
}

function toggleSelectedUploadDate(date: string) {
  selectedUploadDate.value = selectedUploadDate.value === date ? null : date
}

function clearSelectedUploadDate() {
  selectedUploadDate.value = null
}

function setSelectedCloudIds(nextIds: Iterable<string>) {
  selectedCloudIds.value = new Set(nextIds)
}

function toggleCloudSelection(cloudId: string, checked: boolean) {
  const nextIds = new Set(selectedCloudIds.value)
  if (checked) {
    nextIds.add(cloudId)
  } else {
    nextIds.delete(cloudId)
  }
  setSelectedCloudIds(nextIds)
}

function clearCloudSelection() {
  setSelectedCloudIds([])
}

function toggleSelectionMode() {
  selectionMode.value = !selectionMode.value
  if (!selectionMode.value) {
    clearCloudSelection()
  }
}

function formatDatetimeLocal(iso: string | null) {
  if (!iso) return ''
  const date = new Date(iso)
  const pad = (value: number) => String(value).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`
}

function openEditModal(cloud: ProfileCloudItem) {
  manageError.value = ''
  editInitialValue.value = {
    cloudCategoryId: cloud.cloud_type_id ?? (cloud.custom_cloud_type ? 'other' : null),
    customCloudType: cloud.custom_cloud_type || '',
    capturedAt: formatDatetimeLocal(cloud.captured_at),
    latitude: cloud.latitude,
    longitude: cloud.longitude,
    locationName: cloud.location_name || '',
    description: cloud.description || '',
    isPublic: !cloud.is_hidden,
  }
  editModalOpen.value = true
}

function closeEditModal() {
  editModalOpen.value = false
  editSaving.value = false
  manageError.value = ''
}

function blurCoordinate(value: number): number {
  return Math.round(value * 100) / 100
}

async function submitEditModal(value: CloudEditFormValue) {
  if (!selectedCloud.value || !viewedUserId.value) return

  if (!value.cloudCategoryId) {
    manageError.value = '请选择云型类别。'
    return
  }
  if (value.cloudCategoryId === 'other' && !value.customCloudType.trim()) {
    manageError.value = '请输入自定义云型名称。'
    return
  }
  if (!value.capturedAt) {
    manageError.value = '请选择拍摄时间。'
    return
  }
  const hasLat = typeof value.latitude === 'number' && !Number.isNaN(value.latitude)
  const hasLng = typeof value.longitude === 'number' && !Number.isNaN(value.longitude)
  if (hasLat !== hasLng) {
    manageError.value = '经纬度必须同时填写。'
    return
  }

  editSaving.value = true
  manageError.value = ''

  try {
    const updated = await profileStore.updateCloud(viewedUserId.value, selectedCloud.value.id, {
      cloud_type_id: value.cloudCategoryId === 'other' ? null : value.cloudCategoryId,
      custom_cloud_type: value.cloudCategoryId === 'other' ? value.customCloudType.trim() : null,
      latitude: hasLat ? blurCoordinate(value.latitude as number) : null,
      longitude: hasLng ? blurCoordinate(value.longitude as number) : null,
      location_name: value.locationName.trim() || null,
      description: value.description.trim() || null,
      captured_at: new Date(value.capturedAt).toISOString(),
      is_hidden: !value.isPublic,
    })
    selectedCloud.value = updated
    closeEditModal()
  } catch (error) {
    manageError.value = error instanceof Error ? error.message : '图片信息更新失败'
  } finally {
    editSaving.value = false
  }
}

async function toggleSelectedCloudPrivacy() {
  if (!selectedCloud.value || !viewedUserId.value) return

  manageError.value = ''
  const nextHidden = !selectedCloud.value.is_hidden

  try {
    await profileStore.updateCloudVisibility(viewedUserId.value, selectedCloud.value.id, nextHidden)
    selectedCloud.value = { ...selectedCloud.value, is_hidden: nextHidden }
  } catch (error) {
    manageError.value = error instanceof Error ? error.message : '私密状态更新失败'
  }
}

async function deleteCloudByIds(cloudIds: string[]) {
  if (!viewedUserId.value || !cloudIds.length) return
  const confirmed = window.confirm(`确定删除 ${cloudIds.length} 张图片吗？删除后无法在页面中恢复。`)
  if (!confirmed) return

  manageError.value = ''

  try {
    const deletedCount = await profileStore.deleteClouds(viewedUserId.value, cloudIds)
    if (selectedCloud.value && cloudIds.includes(selectedCloud.value.id)) {
      selectedCloud.value = null
      closeEditModal()
    }
    const nextSelected = new Set(selectedCloudIds.value)
    for (const cloudId of cloudIds) nextSelected.delete(cloudId)
    setSelectedCloudIds(nextSelected)
    message.success(`已成功删除 ${deletedCount} 张照片`)
  } catch (error) {
    manageError.value = error instanceof Error ? error.message : '图片删除失败'
  }
}

function handleCloudAction(key: string | number) {
  if (!selectedCloud.value) return
  if (key === 'edit') openEditModal(selectedCloud.value)
  if (key === 'delete') deleteCloudByIds([selectedCloud.value.id])
  if (key === 'toggle-privacy') toggleSelectedCloudPrivacy()
}

async function loadProfilePage(force = false) {
  selectedCloud.value = null
  selectedUploadDate.value = null
  clearCloudSelection()
  selectionMode.value = false

  const targetUserId = viewedUserId.value

  if (!targetUserId) {
    return
  }

  await profileStore.fetchProfilePage(targetUserId, isOwnProfile.value, force)

  if (isOwnProfile.value) {
    await cloudsStore.fetchCloudTypes()
    await encyclopediaStore.fetchCloudTypes(force)
    await encyclopediaStore.fetchMyCollection(force)
  }
}

watch(
  () => [route.params.id, authStore.user?.id],
  () => {
    loadProfilePage()
  },
  { immediate: true },
)

watch(selectedUploadDate, async newValue => {
  if (!newValue) return
  await nextTick()
  timelineSectionRef.value?.scrollIntoView({
    behavior: 'smooth',
    block: 'start',
  })
})
</script>

<template>
  <div class="relative">
    <div class="border-b border-sky-100 bg-[linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)]">
      <div class="max-w-6xl mx-auto px-4 py-10">
        <div v-if="loading" class="grid gap-6 lg:grid-cols-[1.25fr_0.95fr]">
          <NCard class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
            <NSkeleton class="h-6 w-1/3" />
            <NSkeleton class="mt-4 h-10 w-2/3" />
            <NSkeleton class="mt-5 h-4 w-full" :repeat="2" />
          </NCard>
          <NCard class="oc-panel-card-soft border border-white/80 bg-white/85 shadow-sm backdrop-blur" :bordered="false">
            <NSkeleton class="h-5 w-1/3" />
            <NSkeleton class="mt-4 h-8 w-1/2" />
            <NSkeleton class="mt-5 h-3 w-full" />
          </NCard>
        </div>

        <div v-else class="grid gap-6 lg:grid-cols-[1.25fr_0.95fr] lg:items-start">
          <div class="flex gap-5">
            <div
              class="flex h-20 w-20 shrink-0 items-center justify-center border border-slate-300 bg-[linear-gradient(135deg,#ecfeff_0%,#ccfbf1_100%)] text-3xl font-bold text-slate-900 shadow-[4px_4px_0_0_rgba(15,23,42,0.08)]"
            >
              <img
                v-if="profileData?.avatar_url"
                :src="profileData.avatar_url"
                :alt="profileData.username"
                class="h-full w-full object-cover"
              />
              <template v-else>
                {{ getProfileInitial(profileData) }}
              </template>
            </div>

            <div class="min-w-0">
              <p class="text-sm font-medium uppercase tracking-[0.24em] text-sky-700">Sky Journal</p>
              <h1 class="mt-3 text-4xl font-bold text-slate-900">
                {{ pageTitle }}
              </h1>
              <div class="mt-3 flex flex-wrap items-center gap-3">
                <p class="text-lg font-medium text-slate-700">
                  @{{ profileData?.username || '未知用户' }}
                </p>
                <RouterLink v-if="isOwnProfile" to="/profile/settings">
                  <button
                    type="button"
                    class="inline-flex h-8 w-8 items-center justify-center border border-slate-300 bg-white text-slate-600 transition-colors hover:border-slate-900 hover:text-slate-900"
                    title="个人资料设置"
                    aria-label="个人资料设置"
                  >
                    <NIcon size="17">
                      <Settings />
                    </NIcon>
                  </button>
                </RouterLink>
              </div>
              <p class="mt-4 max-w-2xl text-sm leading-7 text-slate-600">
                {{ profileSubtitle }}
              </p>
              <div class="mt-5 flex flex-wrap items-center gap-3 text-sm text-slate-500">
                <span>注册于 {{ formatDate(profileData?.created_at || null) }}</span>
                <NTag v-if="isOwnProfile" type="primary" :bordered="false">你的主页</NTag>
                <NTag v-else :bordered="false">公开展示</NTag>
              </div>
            </div>
          </div>

          <EncyclopediaProgressCard
            v-if="isOwnProfile"
            :unlocked-count="encyclopediaStore.unlockedCount"
            :total-count="encyclopediaStore.cloudTypes.length || 10"
            :percent="encyclopediaStore.unlockPercent"
            :is-logged-in="true"
          />

          <NCard v-else class="oc-panel-card-soft border border-white/80 bg-white/85 shadow-sm backdrop-blur" :bordered="false">
            <p class="text-sm text-slate-500">公开贡献</p>
            <p class="mt-3 text-3xl font-bold text-slate-900">{{ approvedShots }}</p>
            <p class="mt-2 text-sm text-slate-500">当前公开可见的云图数量</p>
          </NCard>
        </div>
      </div>
    </div>

    <div class="max-w-6xl mx-auto px-4 py-8">
      <NAlert
        v-if="errorMsg"
        type="error"
        :show-icon="false"
        :bordered="false"
        title="个人主页加载失败"
      >
        {{ errorMsg }}
      </NAlert>

      <NAlert
        v-if="manageError"
        class="mb-5"
        type="error"
        :show-icon="false"
        :bordered="false"
        title="操作失败"
      >
        {{ manageError }}
      </NAlert>

      <template v-else>
        <div v-if="loading" class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
          <NCard v-for="n in 4" :key="n">
            <NSkeleton class="h-4 w-1/3" />
            <NSkeleton class="mt-4 h-8 w-2/3" />
          </NCard>
        </div>

        <div v-else class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
          <NCard class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
            <p class="text-sm text-slate-500">总拍摄数</p>
            <p class="mt-2 text-3xl font-bold text-slate-900">{{ totalShots }}</p>
          </NCard>

          <NCard class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
            <p class="text-sm text-slate-500">最常拍云型</p>
            <p class="mt-2 text-2xl font-bold text-slate-900">{{ mostCommonCloudType.name }}</p>
            <p class="mt-2 text-sm text-slate-500" v-if="mostCommonCloudType.count">
              共 {{ mostCommonCloudType.count }} 张
            </p>
          </NCard>

          <NCard class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
            <p class="text-sm text-slate-500">拍摄天数</p>
            <p class="mt-2 text-3xl font-bold text-slate-900">{{ shootingDays }}</p>
          </NCard>

          <NCard class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
            <p class="text-sm text-slate-500">{{ isOwnProfile ? '已通过 / 待审核' : '公开通过数' }}</p>
            <p class="mt-2 text-2xl font-bold text-slate-900">
              <template v-if="isOwnProfile">{{ approvedShots }} / {{ pendingShots }}</template>
              <template v-else>{{ approvedShots }}</template>
            </p>
          </NCard>
        </div>

        <section ref="timelineSectionRef" class="mt-8">
          <ContributionHeatmap
            :cells="contributionCells"
            :total-count="totalShots"
            :active-days="activeUploadDays"
            :current-streak="currentUploadStreak"
            :best-streak="longestUploadStreak"
            :active-date="selectedUploadDate"
            :subtitle="isOwnProfile ? '按上传日期统计' : '按公开上传日期统计'"
            :title="isOwnProfile ? '上传活跃度' : '公开活跃度'"
            @select-date="toggleSelectedUploadDate"
          />
        </section>

        <section class="mt-8">
          <div class="mb-5 flex items-end justify-between gap-4">
            <div>
              <h2 class="text-2xl font-bold text-slate-900">天空日志</h2>
              <p class="mt-1 text-sm text-slate-500">
                按拍摄时间倒序展示，并按月份分组。热力图统计按上传日期计算。
              </p>
            </div>
            <div class="flex items-center gap-3">
              <NButton
                v-if="isOwnProfile && selectedCloudCount"
                secondary
                strong
                type="default"
                class="oc-panel-button oc-panel-button--danger"
                @click="deleteCloudByIds(Array.from(selectedCloudIds))"
              >
                删除已选 {{ selectedCloudCount }}
              </NButton>
              <NButton
                v-if="isOwnProfile"
                secondary
                strong
                class="oc-panel-button oc-panel-button--neutral"
                @click="toggleSelectionMode"
              >
                {{ selectionMode ? '取消选择' : '选择图片' }}
              </NButton>
              <NButton secondary strong class="oc-panel-button oc-panel-button--neutral" @click="loadProfilePage(true)">
                刷新数据
              </NButton>
              <NButton
                v-if="selectedUploadDate"
                secondary
                strong
                class="oc-panel-button oc-panel-button--neutral"
                @click="clearSelectedUploadDate"
              >
                清除筛选
              </NButton>
              <RouterLink v-if="isOwnProfile" to="/upload">
                <NButton secondary strong type="default" class="oc-panel-button oc-panel-button--sky">继续上传</NButton>
              </RouterLink>
            </div>
          </div>

          <div
            v-if="selectedUploadDate"
            class="mb-5 border border-sky-200 bg-sky-50 px-4 py-3 text-sm text-sky-800"
          >
            当前正在查看 <span class="font-semibold">{{ formatDayKey(selectedUploadDate) }}</span> 上传的图片，
            共 <span class="font-semibold">{{ selectedUploadCount }}</span> 张。
          </div>

          <div v-if="loading" class="space-y-8">
            <div v-for="n in 2" :key="n">
              <NSkeleton class="h-6 w-40" />
              <div class="mt-4 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
                <NCard v-for="m in 3" :key="m" class="oc-panel-card border border-slate-200 shadow-sm" :bordered="false">
                  <NSkeleton class="h-52 w-full" />
                  <NSkeleton class="mt-4 h-5 w-2/3" />
                  <NSkeleton class="mt-3 h-4 w-1/2" />
                </NCard>
              </div>
            </div>
          </div>

          <div v-else-if="timelineGroups.length" class="space-y-10">
            <section v-for="group in timelineGroups" :key="group.key">
              <div class="flex items-center gap-4">
                <h3 class="text-xl font-bold text-slate-900">{{ group.label }}</h3>
                <div class="h-px flex-1 bg-slate-200"></div>
              </div>

              <div class="mt-5 grid gap-5 md:grid-cols-2 xl:grid-cols-3">
                <button
                  v-for="item in group.items"
                  :key="item.id"
                  type="button"
                  @click="selectedCloud = item"
                  class="group overflow-hidden border bg-white text-left shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg"
                  :class="selectedUploadDate && toDayKey(item.created_at) === selectedUploadDate ? 'border-sky-500 shadow-sky-100' : 'border-slate-200'"
                >
                  <div class="relative">
                    <img
                      :src="item.thumbnail_url || item.image_url"
                      :alt="item.cloudTypeName"
                      class="h-56 w-full object-cover transition duration-500 group-hover:scale-[1.03]"
                    />
                    <div class="absolute inset-x-0 bottom-0 h-28 bg-gradient-to-t from-slate-950/82 via-slate-950/32 to-transparent"></div>
                    <div class="absolute left-4 top-4 flex flex-wrap gap-2">
                      <label
                        v-if="isOwnProfile && selectionMode"
                        class="inline-flex items-center border border-white/60 bg-white/90 px-2 py-1 text-xs font-medium text-slate-700"
                        @click.stop
                      >
                        <input
                          type="checkbox"
                          class="mr-2 h-3.5 w-3.5 border-slate-300 text-sky-500 focus:ring-sky-500"
                          :checked="selectedCloudIds.has(item.id)"
                          @change="toggleCloudSelection(item.id, ($event.target as HTMLInputElement).checked)"
                        />
                        选择
                      </label>
                      <span class="inline-flex border px-3 py-1 text-xs font-medium" :class="rarityMeta[item.cloudTypeRarity].chip">
                        {{ rarityMeta[item.cloudTypeRarity].label }}
                      </span>
                      <span
                        v-if="isOwnProfile"
                        class="inline-flex border px-3 py-1 text-xs font-medium"
                        :class="statusMeta[item.status].chip"
                      >
                        {{ statusMeta[item.status].label }}
                      </span>
                      <span
                        v-if="isOwnProfile"
                        class="inline-flex border px-3 py-1 text-xs font-medium"
                        :class="item.is_hidden ? 'border-slate-200 bg-slate-100 text-slate-600' : 'border-emerald-200 bg-emerald-100 text-emerald-700'"
                      >
                        {{ item.is_hidden ? '私密' : '公开' }}
                      </span>
                    </div>
                    <div class="absolute bottom-4 left-4 right-4 text-white">
                      <p class="truncate text-lg font-semibold">{{ item.cloudTypeName }}</p>
                      <p class="mt-1 truncate text-xs text-white/80">{{ formatCloudTime(item) }}</p>
                    </div>
                  </div>

                  <div class="p-5">
                    <p class="truncate text-sm font-medium text-slate-900">
                      {{ item.location_name || '未填写位置名称' }}
                    </p>
                    <p class="mt-3 line-clamp-3 text-sm leading-6 text-slate-500">
                      {{ item.description || '没有留下额外说明。' }}
                    </p>
                  </div>
                </button>
              </div>
            </section>
          </div>

          <div v-else class="oc-empty-card border border-dashed border-slate-300 bg-white p-10">
            <NEmpty :description="selectedUploadDate ? '这一天没有上传记录' : (isOwnProfile ? '还没有上传任何云图' : '这位用户还没有公开云图')">
              <template #extra>
                <RouterLink v-if="isOwnProfile && !selectedUploadDate" to="/upload">
                  <NButton secondary strong type="default" class="oc-panel-button oc-panel-button--sky">去上传第一张</NButton>
                </RouterLink>
                <NButton v-else-if="selectedUploadDate" secondary strong class="oc-panel-button oc-panel-button--neutral" @click="clearSelectedUploadDate">返回全部记录</NButton>
              </template>
            </NEmpty>
          </div>
        </section>
      </template>
    </div>

    <ImageDetailModal
      v-if="selectedCloud"
      :open="!!selectedCloud"
      :image-url="selectedCloud.image_url"
      :thumbnail-url="selectedCloud.thumbnail_url"
      :image-alt="selectedCloud.cloudTypeName"
      :title="selectedCloud.cloudTypeName"
      :subtitle="`拍摄时间：${formatCloudTime(selectedCloud)}`"
      :badge-label="rarityMeta[selectedCloud.cloudTypeRarity].label"
      :badge-class="rarityMeta[selectedCloud.cloudTypeRarity].chip"
      @close="selectedCloud = null"
    >
      <div class="grid gap-4 sm:grid-cols-2">
        <div class="border border-slate-200 bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">上传时间</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ formatDateTime(selectedCloud.created_at) }}</p>
        </div>
        <div class="border border-slate-200 bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">拍摄时间</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ formatDateTime(selectedCloud.captured_at) }}</p>
        </div>
        <div class="border border-slate-200 bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">位置</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ selectedCloud.location_name || '未填写位置名称' }}</p>
        </div>
        <div class="border border-slate-200 bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">模糊化经纬度</p>
          <p class="mt-2 text-sm font-medium text-slate-900">
            纬度 {{ formatCoordinate(selectedCloud.latitude) }} / 经度 {{ formatCoordinate(selectedCloud.longitude) }}
          </p>
        </div>
        <div class="border border-slate-200 bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">{{ isOwnProfile ? '审核状态' : '可见性' }}</p>
          <p class="mt-2 text-sm font-medium text-slate-900">
            <template v-if="isOwnProfile">
              {{ statusMeta[selectedCloud.status].label }} / {{ selectedCloud.is_hidden ? '私密' : '公开' }}
            </template>
            <template v-else>公开可见</template>
          </p>
        </div>
      </div>

      <div class="mt-5 border border-slate-200 bg-white p-5">
        <p class="text-xs uppercase tracking-[0.18em] text-slate-500">图片说明</p>
        <p class="mt-3 text-sm leading-7 text-slate-700">
          {{ selectedCloud.description || '上传者没有留下额外说明。' }}
        </p>
      </div>
      <MiniLocationMap
        :latitude="selectedCloud.latitude"
        :longitude="selectedCloud.longitude"
        :location-name="selectedCloud.location_name"
      />

      <template v-if="isOwnProfile" #actions>
        <div class="flex items-center justify-between gap-3">
          <NDropdown
            trigger="click"
            placement="top-start"
            :options="cloudActionOptions"
            @select="handleCloudAction"
          >
            <button
              type="button"
              class="flex h-10 w-10 items-center justify-center border border-slate-200 bg-slate-50 text-slate-600 transition-colors hover:border-slate-900 hover:text-slate-900"
              title="图片管理"
              aria-label="图片管理"
            >
              <NIcon size="20">
                <Settings />
              </NIcon>
            </button>
          </NDropdown>
          <!-- <span class="text-xs text-slate-500">
            {{ selectedCloud.is_hidden ? '当前为私密图片' : '当前会公开展示' }}
          </span> -->
        </div>
      </template>
    </ImageDetailModal>

    <CloudEditModal
      :open="editModalOpen && !!selectedCloud"
      :saving="editSaving"
      :error="manageError"
      :cloud-types="cloudsStore.cloudTypes"
      :initial-value="editInitialValue"
      @close="closeEditModal"
      @save="submitEditModal"
    />
  </div>
</template>
