<script setup lang="ts">
usePageTitle('RSS 收件箱')

type Feed = {
  id: number
  feedUrl: string
  title: string | null
  lastError: string | null
  lastFetchedAt: string | null
  pollIntervalMinutes: number
  nextSyncAt: string | null
}
type RssSyncMeta = { serverCheckIntervalMinutes: number }
type Item = {
  id: number
  title: string | null
  canonicalUrl: string
  visibility: string
  feedId: number
  shareToken: string | null
}

const feeds = ref<Feed[]>([])
const syncMeta = ref<RssSyncMeta>({ serverCheckIntervalMinutes: 60 })
const items = ref<Item[]>([])
const feedUrl = ref('')
const loading = ref(true)
const selectedFeedId = ref<number | null>(null)
const copiedItemId = ref<number | null>(null)
const readingMode = ref(false)
const activeItemId = ref<number | null>(null)
let copyResetTimer: ReturnType<typeof setTimeout> | undefined

const { user, refresh } = useAuthSession()
const { fetchData } = useClientApi()
const toast = useToast()

const filteredItems = computed(() => {
  if (selectedFeedId.value === null) {
    return items.value
  }
  return items.value.filter((it) => it.feedId === selectedFeedId.value)
})

const selectedFeedLabel = computed(() => {
  if (selectedFeedId.value === null) {
    return '全部订阅'
  }
  const f = feeds.value.find((x) => x.id === selectedFeedId.value)
  return f?.title || f?.feedUrl || '当前订阅'
})

const activeItemIndex = computed(() => {
  if (activeItemId.value === null) {
    return -1
  }
  return filteredItems.value.findIndex((it) => it.id === activeItemId.value)
})

const activeItem = computed(() => {
  if (activeItemIndex.value < 0) {
    return null
  }
  return filteredItems.value[activeItemIndex.value] ?? null
})

const hasPrevItem = computed(() => activeItemIndex.value > 0)
const hasNextItem = computed(() => activeItemIndex.value >= 0 && activeItemIndex.value < filteredItems.value.length - 1)

function formatNextSync(iso: string) {
  return new Date(iso).toLocaleString('zh-CN', { dateStyle: 'short', timeStyle: 'short' })
}

const earliestNextSyncLabel = computed(() => {
  const dates = feeds.value.map((f) => f.nextSyncAt).filter(Boolean) as string[]
  if (!dates.length) {
    return null
  }
  return formatNextSync(dates.reduce((a, b) => (a < b ? a : b)))
})

function feedNextLine(f: Feed) {
  if (!f.nextSyncAt) {
    return '尚未完成首次抓取，后台将自动重试'
  }
  return `预计下次同步：${formatNextSync(f.nextSyncAt)}`
}

function countForFeed(feedId: number) {
  return items.value.filter((it) => it.feedId === feedId).length
}

function feedLabelFor(feedId: number) {
  const f = feeds.value.find((x) => x.id === feedId)
  return f?.title || f?.feedUrl || '未知来源'
}

function unlistedSharePath(publicSlug: string, token: string) {
  return `/p/${encodeURIComponent(publicSlug)}/t/${encodeURIComponent(token)}`
}

function unlistedShareFullUrl(publicSlug: string, token: string) {
  if (import.meta.client) {
    return `${window.location.origin}${unlistedSharePath(publicSlug, token)}`
  }
  return unlistedSharePath(publicSlug, token)
}

async function load() {
  loading.value = true
  try {
    const [f, i] = await Promise.all([
      fetchData<{ feeds: Feed[]; sync: RssSyncMeta }>('/api/me/rss/feeds'),
      fetchData<{ items: Item[] }>('/api/me/rss/items'),
    ])
    syncMeta.value = f.sync
    feeds.value = f.feeds
    items.value = i.items
    if (selectedFeedId.value !== null && !feeds.value.some((x) => x.id === selectedFeedId.value)) {
      selectedFeedId.value = null
    }
  } finally {
    loading.value = false
  }
}

onMounted(async () => {
  await Promise.all([refresh(), load()])
})

async function addFeed() {
  await fetchData('/api/me/rss/feeds', { method: 'POST', body: { feedUrl: feedUrl.value } })
  feedUrl.value = ''
  await load()
  toast.add({ title: '已添加订阅', color: 'success' })
}

async function syncAll() {
  await fetchData('/api/me/rss/sync', { method: 'POST', body: {} })
  await load()
  toast.add({ title: '同步完成', color: 'success' })
}

async function removeFeed(id: number) {
  await fetchData(`/api/me/rss/feeds/${id}`, { method: 'DELETE' })
  if (selectedFeedId.value === id) {
    selectedFeedId.value = null
  }
  await load()
  toast.add({ title: '已删除订阅', color: 'success' })
}

async function setItemVis(id: number, visibility: string) {
  const idx = items.value.findIndex((it) => it.id === id)
  if (idx < 0) {
    return
  }

  const prev = items.value[idx].visibility
  if (prev === visibility) {
    return
  }

  // 乐观更新：避免整页重新加载导致的视觉抖动和滚动跳动
  items.value[idx].visibility = visibility
  try {
    await fetchData(`/api/me/rss/items/${id}`, { method: 'PATCH', body: { visibility } })
    toast.add({ title: '可见性已更新', color: 'success' })
  } catch {
    items.value[idx].visibility = prev
    toast.add({ title: '更新失败，已恢复原状态', color: 'error' })
  }
}

async function copyUnlistedLink(it: Item) {
  const slug = user.value?.publicSlug?.trim()
  if (!slug || !it.shareToken) {
    return
  }
  const text = unlistedShareFullUrl(slug, it.shareToken)
  try {
    await navigator.clipboard.writeText(text)
    copiedItemId.value = it.id
    if (copyResetTimer) {
      clearTimeout(copyResetTimer)
    }
    copyResetTimer = setTimeout(() => {
      copiedItemId.value = null
    }, 2000)
  } catch {
    // ignore
  }
}

function openReadingMode() {
  readingMode.value = true
  if (!filteredItems.value.length) {
    activeItemId.value = null
    return
  }
  if (!filteredItems.value.some((it) => it.id === activeItemId.value)) {
    activeItemId.value = filteredItems.value[0].id
  }
}

function closeReadingMode() {
  readingMode.value = false
}

function selectPrevItem() {
  if (!hasPrevItem.value) {
    return
  }
  const prev = filteredItems.value[activeItemIndex.value - 1]
  if (prev) {
    activeItemId.value = prev.id
  }
}

function selectNextItem() {
  if (!hasNextItem.value) {
    return
  }
  const next = filteredItems.value[activeItemIndex.value + 1]
  if (next) {
    activeItemId.value = next.id
  }
}

watch(filteredItems, (list) => {
  if (!list.length) {
    activeItemId.value = null
    return
  }
  if (!list.some((it) => it.id === activeItemId.value)) {
    activeItemId.value = list[0].id
  }
}, { immediate: true })
</script>

<template>
  <UContainer class="py-8 space-y-6 max-w-6xl">
    <div class="relative overflow-hidden rounded-2xl border border-primary/30 bg-gradient-to-br from-primary/15 via-default to-elevated px-6 py-6">
      <div class="pointer-events-none absolute -right-14 -top-16 h-48 w-48 rounded-full bg-primary/20 blur-3xl" />
      <div class="pointer-events-none absolute -left-20 -bottom-20 h-56 w-56 rounded-full bg-primary/10 blur-3xl" />
      <div class="relative space-y-4">
        <div>
          <p class="text-xs uppercase tracking-[0.2em] text-primary/80">
            Reading Inbox
          </p>
          <h1 class="mt-1 text-2xl font-semibold text-highlighted sm:text-3xl">
            RSS 收件箱
          </h1>
          <p class="mt-2 text-sm text-muted max-w-3xl">
            服务器约每 {{ syncMeta.serverCheckIntervalMinutes }} 分钟检查一次到期订阅，新增订阅会立即抓取一次。
            <template v-if="!loading && feeds.length && earliestNextSyncLabel">
              当前最早下次同步时间：{{ earliestNextSyncLabel }}。
            </template>
          </p>
        </div>

        <div class="grid gap-2 sm:grid-cols-3">
          <div class="rounded-xl border border-default bg-default/90 px-3 py-2">
            <p class="text-xs text-muted">
              订阅源
            </p>
            <p class="text-lg font-semibold text-highlighted">
              {{ feeds.length }}
            </p>
          </div>
          <div class="rounded-xl border border-default bg-default/90 px-3 py-2">
            <p class="text-xs text-muted">
              全部条目
            </p>
            <p class="text-lg font-semibold text-highlighted">
              {{ items.length }}
            </p>
          </div>
          <div class="rounded-xl border border-default bg-default/90 px-3 py-2">
            <p class="text-xs text-muted">
              当前筛选
            </p>
            <p class="truncate text-lg font-semibold text-highlighted" :title="selectedFeedLabel">
              {{ selectedFeedLabel }}
            </p>
          </div>
        </div>
      </div>
    </div>

    <UCard class="border-primary/20">
      <div class="flex flex-col gap-2 sm:flex-row">
        <UInput v-model="feedUrl" placeholder="https://example.com/feed.xml" class="flex-1" />
        <UButton icon="i-lucide-plus" @click="addFeed">
          添加订阅
        </UButton>
        <UButton color="neutral" variant="outline" icon="i-lucide-refresh-cw" @click="syncAll">
          全部同步
        </UButton>
      </div>
    </UCard>

    <div v-if="loading" class="rounded-xl border border-dashed border-default p-6 text-sm text-muted">
      加载中…
    </div>
    <div v-else-if="readingMode" class="space-y-4">
      <UCard class="border-primary/25">
        <div class="flex flex-wrap items-center justify-between gap-2">
          <div>
            <p class="text-xs text-muted">
              阅读模式（信息密集）
            </p>
            <p class="text-sm font-medium text-highlighted">
              {{ selectedFeedLabel }} · {{ filteredItems.length }} 条
            </p>
          </div>
          <div class="flex gap-2">
            <UButton size="sm" color="neutral" variant="outline" :disabled="!hasPrevItem" @click="selectPrevItem">
              上一条
            </UButton>
            <UButton size="sm" color="neutral" variant="outline" :disabled="!hasNextItem" @click="selectNextItem">
              下一条
            </UButton>
            <UButton size="sm" color="neutral" variant="soft" @click="closeReadingMode">
              退出阅读模式
            </UButton>
          </div>
        </div>
      </UCard>

      <div class="grid gap-4 lg:grid-cols-[360px_minmax(0,1fr)]">
        <UCard class="border-default/80">
          <template #header>
            <div class="flex items-center justify-between">
              <span class="font-medium">目录</span>
              <UBadge size="sm" color="neutral" variant="soft">
                {{ filteredItems.length }} 条
              </UBadge>
            </div>
          </template>
          <UEmpty v-if="!filteredItems.length" title="暂无条目" />
          <ul v-else class="space-y-1.5 max-h-[70vh] overflow-auto pr-1">
            <li v-for="it in filteredItems" :key="it.id">
              <button
                type="button"
                class="w-full rounded-lg border px-3 py-2 text-left transition-all"
                :class="activeItemId === it.id
                  ? 'border-primary/60 bg-primary/12 ring-1 ring-primary/35'
                  : 'border-default hover:border-primary/30 hover:bg-elevated/55'"
                @click="activeItemId = it.id"
              >
                <p class="line-clamp-2 text-sm font-medium text-highlighted">
                  {{ it.title || '未命名条目' }}
                </p>
                <p class="mt-1 truncate text-[11px] text-muted">
                  {{ feedLabelFor(it.feedId) }}
                </p>
              </button>
            </li>
          </ul>
        </UCard>

        <UCard class="border-default/80">
          <template #header>
            <div class="flex flex-wrap items-center justify-between gap-2">
              <div>
                <p class="text-xs text-muted">
                  当前阅读
                </p>
                <p class="text-sm font-medium text-highlighted">
                  {{ activeItemIndex + 1 > 0 ? `${activeItemIndex + 1} / ${filteredItems.length}` : '未选择' }}
                </p>
              </div>
              <UButton
                v-if="activeItem"
                size="sm"
                color="neutral"
                variant="outline"
                :href="activeItem.canonicalUrl"
                target="_blank"
                rel="noopener noreferrer"
              >
                打开原文
              </UButton>
            </div>
          </template>
          <UEmpty v-if="!activeItem" title="暂无可阅读条目" />
          <article v-else class="space-y-3">
            <h2 class="text-xl font-semibold leading-snug text-highlighted">
              {{ activeItem.title || '未命名条目' }}
            </h2>
            <div class="flex flex-wrap gap-2 text-xs">
              <UBadge color="neutral" variant="soft">
                来源：{{ feedLabelFor(activeItem.feedId) }}
              </UBadge>
              <UBadge color="neutral" variant="subtle">
                可见性：{{ activeItem.visibility === 'private' ? '私密' : activeItem.visibility === 'public' ? '公开' : '仅链接' }}
              </UBadge>
            </div>
            <p class="text-xs break-all text-muted">
              {{ activeItem.canonicalUrl }}
            </p>
            <div class="rounded-lg border border-default bg-elevated/30 px-3 py-3">
              <p class="text-xs leading-relaxed text-muted">
                该模式聚焦连续阅读与快速切换，管理操作已收敛。若要批量调整可见性或管理订阅，请退出阅读模式。
              </p>
            </div>
          </article>
        </UCard>
      </div>
    </div>
    <div v-else class="grid gap-6 lg:grid-cols-[320px_minmax(0,1fr)]">
      <UCard class="border-default/80 bg-default/70 backdrop-blur-sm lg:sticky lg:top-4 lg:max-h-[calc(100vh-2rem)]">
        <template #header>
          <div class="flex items-center justify-between">
            <span class="font-medium">订阅源（仅自己可见）</span>
            <UBadge size="sm" variant="subtle" color="neutral">
              {{ feeds.length }} 个
            </UBadge>
          </div>
        </template>
        <UEmpty v-if="!feeds.length" title="暂无订阅" description="添加一个 feed URL 开始聚合" />
        <ul v-else class="space-y-2.5 lg:max-h-[calc(100vh-10rem)] lg:overflow-auto lg:pr-1">
          <li>
            <button
              type="button"
              class="w-full rounded-xl border px-3 py-3 text-left transition-all"
              :class="selectedFeedId === null
                ? 'border-primary/60 bg-primary/12 ring-1 ring-primary/40'
                : 'border-default hover:border-primary/30 hover:bg-elevated/60'"
              @click="selectedFeedId = null"
            >
              <p class="text-sm font-semibold text-highlighted">
                全部订阅
              </p>
              <p class="mt-0.5 text-xs text-muted">
                {{ items.length }} 条内容
              </p>
            </button>
          </li>
          <li v-for="f in feeds" :key="f.id">
            <div
              class="rounded-xl border p-3 transition-all"
              :class="selectedFeedId === f.id
                ? 'border-primary/60 bg-primary/12 ring-1 ring-primary/40'
                : 'border-default hover:border-primary/30 hover:bg-elevated/60'"
            >
              <button
                type="button"
                class="w-full text-left"
                @click="selectedFeedId = f.id"
              >
                <p class="truncate text-sm font-semibold text-highlighted" :title="f.feedUrl">
                  {{ f.title || f.feedUrl }}
                </p>
                <p class="mt-0.5 text-xs text-muted">
                  {{ countForFeed(f.id) }} 条内容
                </p>
                <p class="mt-2 text-[11px] leading-snug text-muted">
                  {{ feedNextLine(f) }}
                </p>
              </button>
              <p v-if="f.lastError" class="mt-2 rounded-md border border-error/40 bg-error/10 px-2 py-1 text-xs text-error">
                {{ f.lastError }}
              </p>
              <div class="mt-2">
                <UButton size="xs" color="error" variant="ghost" class="-ml-2" @click.stop="removeFeed(f.id)">
                  删除订阅
                </UButton>
              </div>
            </div>
          </li>
        </ul>
      </UCard>

      <UCard class="border-default/80">
        <template #header>
          <div class="flex flex-wrap items-center justify-between gap-2">
            <div>
              <p class="font-medium text-highlighted">
                条目流
              </p>
              <p class="text-xs text-muted">
                当前：{{ selectedFeedLabel }}（{{ filteredItems.length }} 条）
              </p>
            </div>
            <div class="flex items-center gap-2">
              <UBadge size="sm" color="neutral" variant="soft">
                默认私密，可随时切换
              </UBadge>
              <UButton
                size="sm"
                color="neutral"
                variant="outline"
                :disabled="!filteredItems.length"
                @click="openReadingMode"
              >
                阅读模式
              </UButton>
            </div>
          </div>
        </template>
        <UEmpty v-if="!items.length" title="暂无条目" description="添加订阅并点击同步" />
        <UEmpty
          v-else-if="!filteredItems.length"
          title="该订阅下暂无条目"
          description="切换左侧订阅或先执行同步"
        />
        <ul v-else class="space-y-3">
          <li v-for="it in filteredItems" :key="it.id">
            <article class="rounded-xl border border-default bg-default/80 p-4 transition-all hover:border-primary/35 hover:shadow-sm hover:shadow-primary/10">
              <a
                :href="it.canonicalUrl"
                target="_blank"
                rel="noopener noreferrer"
                class="line-clamp-2 text-base font-semibold text-highlighted transition-colors hover:text-primary"
              >{{ it.title || '未命名条目' }}</a>

              <div class="mt-3">
                <p class="text-xs text-muted">
                  可见性
                </p>
                <div class="mt-1.5 flex flex-wrap gap-1.5">
                  <UButton
                    size="xs"
                    :color="it.visibility === 'private' ? 'primary' : 'neutral'"
                    :variant="it.visibility === 'private' ? 'solid' : 'outline'"
                    @click="setItemVis(it.id, 'private')"
                  >
                    私密
                  </UButton>
                  <UButton
                    size="xs"
                    :color="it.visibility === 'public' ? 'primary' : 'neutral'"
                    :variant="it.visibility === 'public' ? 'solid' : 'outline'"
                    @click="setItemVis(it.id, 'public')"
                  >
                    公开
                  </UButton>
                  <UButton
                    size="xs"
                    :color="it.visibility === 'unlisted' ? 'primary' : 'neutral'"
                    :variant="it.visibility === 'unlisted' ? 'solid' : 'outline'"
                    @click="setItemVis(it.id, 'unlisted')"
                  >
                    仅链接
                  </UButton>
                </div>
              </div>

              <div
                v-if="it.visibility === 'unlisted'"
                class="mt-3 space-y-2 rounded-lg border border-default bg-elevated/40 px-3 py-2"
              >
                <p class="text-xs leading-relaxed text-muted">
                  「仅链接」不会出现在公开主页；把下面地址发给他人即可打开本条目的摘要页（无需登录）。
                </p>
                <template v-if="user?.publicSlug?.trim() && it.shareToken">
                  <div class="flex flex-col gap-2 sm:flex-row">
                    <UInput
                      readonly
                      size="sm"
                      class="min-w-0 flex-1 font-mono text-xs"
                      :model-value="unlistedShareFullUrl(user.publicSlug.trim(), it.shareToken)"
                    />
                    <div class="flex shrink-0 gap-1.5">
                      <UButton size="sm" @click="copyUnlistedLink(it)">
                        {{ copiedItemId === it.id ? '已复制' : '复制链接' }}
                      </UButton>
                      <UButton
                        size="sm"
                        color="neutral"
                        variant="outline"
                        :to="unlistedSharePath(user.publicSlug.trim(), it.shareToken)"
                        target="_blank"
                      >
                        打开
                      </UButton>
                    </div>
                  </div>
                </template>
                <UAlert
                  v-else
                  color="warning"
                  variant="subtle"
                  title="无法生成分享地址"
                  description="请先在「个人资料」中设置公开主页标识（/@slug），保存后再将本条设为「仅链接」。"
                />
              </div>
            </article>
          </li>
        </ul>
      </UCard>
    </div>
  </UContainer>
</template>
