<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { api } from '../api/client'
import CreateStoryModal from '../components/CreateStoryModal.vue'
import BaseButton from '../components/ui/BaseButton.vue'
import BaseCard from '../components/ui/BaseCard.vue'
import EmptyState from '../components/ui/EmptyState.vue'
import LoadingSpinner from '../components/ui/LoadingSpinner.vue'
import type { GenerationOpsSummary, GenerationProviderAnalytics } from '../types/generation'
import type { VoiceSessionAnalytics, VoiceSessionSummary } from '../types/voiceSession'
import {
  getVoiceSessionNextAction,
  getVoiceSessionNextStep,
} from '../utils/voiceSession'
import {
  getAssetStatusMeta,
  getGenerationStatusMeta,
  isReadableGenerationStatus,
  needsGenerationAttention,
} from '../utils/storyStatus'
import {
  BookOpenIcon,
  ChevronRightIcon,
  ExclamationCircleIcon,
  PhotoIcon,
  PlusIcon,
  SparklesIcon,
} from '@heroicons/vue/24/outline'

interface StoryItem {
  id: number
  title: string
  image_url: string | null
  created_at: string
  mode: string
  generation_status: string
  text_status: string
  image_status: string
  audio_status: string
  last_error: string | null
}

type VoiceAttentionReason = 'pending_confirmation' | 'safety_intervention' | 'failed_turn'
type VoiceStudioFocusTarget = 'confirmation' | 'safety' | 'failed' | 'text'

const router = useRouter()
const stories = ref<StoryItem[]>([])
const providerAnalytics = ref<GenerationProviderAnalytics | null>(null)
const opsSummary = ref<GenerationOpsSummary | null>(null)
const activeVoiceSession = ref<VoiceSessionSummary | null>(null)
const voiceAnalytics = ref<VoiceSessionAnalytics | null>(null)
const loading = ref(true)
const error = ref('')
const showCreateModal = ref(false)
const selectedWindow = ref<'7' | '30' | 'all'>('30')
const selectedVoiceWindow = ref<'7' | '30' | 'all'>('30')
const selectedCapability = ref<'all' | 'text' | 'image' | 'tts' | 'storybook'>('all')

const readableCount = computed(() =>
  stories.value.filter((story) => isReadableGenerationStatus(story.generation_status)).length,
)

const attentionCount = computed(() =>
  stories.value.filter((story) => needsGenerationAttention(story.generation_status)).length,
)
const providerSuccessRate = computed(() => {
  if (!providerAnalytics.value?.total_calls) return null
  return Math.round(
    (providerAnalytics.value.successful_calls / providerAnalytics.value.total_calls) * 100,
  )
})
const topProvider = computed(() => providerAnalytics.value?.by_provider[0] ?? null)
const topFailureReason = computed(() => providerAnalytics.value?.failure_reasons[0] ?? null)
const voiceTurnSuccessRate = computed(() => {
  if (!voiceAnalytics.value) return null
  return Math.round(voiceAnalytics.value.turn_success_rate * 100)
})
const voiceFinalizeRate = computed(() => {
  if (!voiceAnalytics.value) return null
  return Math.round(voiceAnalytics.value.finalize_conversion_rate * 100)
})
const voiceAnalyticsWindowLabel = computed(() =>
  formatWindowLabel(voiceAnalytics.value?.window_days ?? null),
)

function buildProviderAnalyticsPath() {
  const params = new URLSearchParams()
  if (selectedWindow.value !== 'all') {
    params.set('days', selectedWindow.value)
  }
  if (selectedCapability.value !== 'all') {
    params.set('capability', selectedCapability.value)
  }
  const query = params.toString()
  return `/api/generations/provider-analytics${query ? `?${query}` : ''}`
}

function buildVoiceAnalyticsPath() {
  if (selectedVoiceWindow.value === 'all') {
    return '/api/voice-sessions/analytics'
  }
  return `/api/voice-sessions/analytics?days=${selectedVoiceWindow.value}`
}

async function fetchStories() {
  try {
    const [storyList, analytics, ops, activeSession, voiceOverview] = await Promise.all([
      api.get<StoryItem[]>('/api/stories'),
      api.get<GenerationProviderAnalytics>(buildProviderAnalyticsPath()),
      api.get<GenerationOpsSummary>('/api/generations/ops-summary'),
      api.get<VoiceSessionSummary | null>('/api/voice-sessions/active').catch(() => null),
      api.get<VoiceSessionAnalytics>(buildVoiceAnalyticsPath()).catch(() => null),
    ])
    stories.value = storyList
    providerAnalytics.value = analytics
    opsSummary.value = ops
    activeVoiceSession.value = activeSession
    voiceAnalytics.value = voiceOverview
  } catch (e) {
    error.value = e instanceof Error ? e.message : '加载失败'
  } finally {
    loading.value = false
  }
}

function formatDate(dateStr: string) {
  const date = new Date(dateStr)
  const now = new Date()
  const diff = now.getTime() - date.getTime()
  const days = Math.floor(diff / (1000 * 60 * 60 * 24))

  if (days === 0) return '今天'
  if (days === 1) return '昨天'
  if (days < 7) return `${days} 天前`

  return date.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  })
}

function formatWindowLabel(windowDays: number | null | undefined) {
  if (typeof windowDays === 'number') {
    return `最近 ${windowDays} 天`
  }
  return '全部历史'
}

function goToCreate() {
  showCreateModal.value = true
}

function goToVoiceStudio(options?: {
  reason?: VoiceAttentionReason
  sessionId?: string
  focus?: VoiceStudioFocusTarget
}) {
  const query: Record<string, string> = {}
  if (options?.reason) {
    query.filter = 'attention'
    query.reason = options.reason
  }
  if (options?.sessionId) {
    query.session = options.sessionId
  }
  if (options?.focus) {
    query.focus = options.focus
  }
  router.push({ path: '/voice-studio', query })
}

function continueActiveVoiceSession() {
  if (!activeVoiceSession.value) {
    goToVoiceStudio()
    return
  }
  const action = getVoiceSessionNextAction(activeVoiceSession.value)
  if (action.storyId) {
    router.push(`/story/${action.storyId}`)
    return
  }
  goToVoiceStudio({
    reason: action.reason,
    sessionId: activeVoiceSession.value.id,
    focus: action.focus,
  })
}

function getStoryLink(story: StoryItem) {
  return story.mode === 'storybook' ? `/storybook/view/${story.id}` : `/story/${story.id}`
}

function formatLatency(value?: number | null) {
  return typeof value === 'number' ? `${Math.round(value)}ms` : '暂无'
}

function formatCost(value?: number | null) {
  return typeof value === 'number' ? `$${value.toFixed(4)}` : '$0.0000'
}

function formatOutputMode(value: string) {
  switch (value) {
    case 'storybook':
      return '绘本'
    case 'asset_retry':
      return '资源重试'
    case 'asset_generation':
      return '资源生成'
    default:
      return '故事'
  }
}

function setWindow(value: '7' | '30' | 'all') {
  selectedWindow.value = value
}

function setVoiceWindow(value: '7' | '30' | 'all') {
  selectedVoiceWindow.value = value
}

function setCapability(value: 'all' | 'text' | 'image' | 'tts' | 'storybook') {
  selectedCapability.value = value
}

onMounted(() => {
  void fetchStories()

  if (router.currentRoute.value.query.openCreate) {
    showCreateModal.value = true
    router.replace({ query: { ...router.currentRoute.value.query, openCreate: undefined } })
  }
})

watch([selectedWindow, selectedCapability, selectedVoiceWindow], () => {
  void fetchStories()
})
</script>

<template>
  <div class="max-w-6xl mx-auto px-4">
    <div class="flex items-center justify-between mb-8">
      <div>
        <h1 class="text-3xl font-bold gradient-text mb-2">我的故事</h1>
        <p class="text-gray-500">回看每个作品的生成质量、资源状态和可优化点。</p>
      </div>
      <BaseButton @click="goToCreate">
        <SparklesIcon class="h-5 w-5 mr-2" />
        创作新故事
      </BaseButton>
    </div>

    <div v-if="loading" class="py-20">
      <LoadingSpinner text="正在加载内容..." />
    </div>

    <div v-else-if="error" class="py-10">
      <EmptyState
        :icon="ExclamationCircleIcon"
        title="加载失败"
        :description="error"
      />
    </div>

    <div v-else-if="stories.length === 0" class="py-10">
      <EmptyState
        :icon="BookOpenIcon"
        title="从第一个作品开始"
        description="现在还没有故事或绘本，先做一个能完整跑通的版本，后面再持续优化。"
      >
        <template #action>
          <BaseButton @click="goToCreate">
            <PlusIcon class="h-5 w-5 mr-2" />
            创作第一个作品
          </BaseButton>
        </template>
      </EmptyState>
    </div>

    <template v-else>
      <BaseCard
        v-if="activeVoiceSession"
        class="mb-8 border border-purple-100 bg-purple-50/60"
        padding="lg"
      >
        <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <h2 class="text-xl font-bold text-gray-800">语音共创正在进行中</h2>
            <p class="mt-2 text-sm leading-6 text-gray-600">
              最近的语音共创会话仍可继续：
              {{ activeVoiceSession.working_title || '未命名语音会话' }}，
              当前状态 {{ activeVoiceSession.status }}，已完成 {{ activeVoiceSession.total_turns }} 轮。
            </p>
            <p
              v-if="activeVoiceSession.latest_requires_confirmation"
              class="mt-2 text-sm text-amber-700"
            >
              上一轮仍在等待家长确认，建议优先回到语音共创工作台处理。
            </p>
            <p
              v-else-if="activeVoiceSession.latest_safety_message"
              class="mt-2 text-sm text-rose-700"
            >
              最近一轮触发了儿童内容安全兜底，建议回到工作台查看详细记录。
            </p>
            <div
              class="mt-3 rounded-xl border px-3 py-3 text-sm leading-6"
              :class="getVoiceSessionNextStep(activeVoiceSession).toneClass"
            >
              <div class="font-medium">
                建议动作：{{ getVoiceSessionNextStep(activeVoiceSession).label }}
              </div>
              <div class="mt-1 opacity-90">
                {{ getVoiceSessionNextStep(activeVoiceSession).description }}
              </div>
            </div>
          </div>
          <BaseButton @click="continueActiveVoiceSession">
            <SparklesIcon class="h-5 w-5 mr-2" />
            {{ getVoiceSessionNextAction(activeVoiceSession).label }}
          </BaseButton>
        </div>
      </BaseCard>

      <BaseCard
        v-if="voiceAnalytics && voiceAnalytics.total_sessions"
        class="mb-8 border border-violet-100 bg-violet-50/40"
        padding="lg"
      >
        <div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <h2 class="text-xl font-bold text-gray-800">语音共创运营摘要</h2>
            <p class="mt-2 text-sm leading-6 text-gray-600">
              {{ voiceAnalyticsWindowLabel }}，你的语音共创已经累计
              {{ voiceAnalytics.total_sessions }} 个会话、{{ voiceAnalytics.total_turns }} 个 turn。
            </p>
            <p
              v-if="voiceAnalytics.low_confidence_turns || voiceAnalytics.safety_interventions"
              class="mt-2 text-sm text-gray-500"
            >
              低置信度确认 {{ voiceAnalytics.low_confidence_turns }} 次，
              安全介入 {{ voiceAnalytics.safety_interventions }} 次。
            </p>
            <p
              v-if="voiceAnalytics.attention_sessions"
              class="mt-2 text-sm text-amber-700"
            >
              当前仍有 {{ voiceAnalytics.attention_sessions }} 个语音会话建议优先回到工作台处理：
              待确认 {{ voiceAnalytics.confirmation_attention_sessions }} 个，
              安全介入 {{ voiceAnalytics.safety_attention_sessions }} 个，
              失败待处理 {{ voiceAnalytics.failed_attention_sessions }} 个。
            </p>
            <div
              v-if="voiceAnalytics.attention_sessions"
              class="mt-3 flex flex-wrap gap-2"
            >
              <button
                v-if="voiceAnalytics.confirmation_attention_sessions"
                type="button"
                class="rounded-lg border border-amber-200 bg-amber-50 px-3 py-1.5 text-sm text-amber-700 transition-colors hover:border-amber-400"
                @click="goToVoiceStudio({ reason: 'pending_confirmation', focus: 'confirmation' })"
              >
                查看待确认会话
              </button>
              <button
                v-if="voiceAnalytics.safety_attention_sessions"
                type="button"
                class="rounded-lg border border-rose-200 bg-rose-50 px-3 py-1.5 text-sm text-rose-700 transition-colors hover:border-rose-400"
                @click="goToVoiceStudio({ reason: 'safety_intervention', focus: 'safety' })"
              >
                查看安全介入会话
              </button>
              <button
                v-if="voiceAnalytics.failed_attention_sessions"
                type="button"
                class="rounded-lg border border-slate-200 bg-slate-100 px-3 py-1.5 text-sm text-slate-700 transition-colors hover:border-slate-400"
                @click="goToVoiceStudio({ reason: 'failed_turn', focus: 'failed' })"
              >
                查看失败待处理会话
              </button>
            </div>
            <div class="mt-4 flex flex-wrap gap-2">
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedVoiceWindow === '7' ? 'border-violet-700 bg-violet-700 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setVoiceWindow('7')">最近 7 天</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedVoiceWindow === '30' ? 'border-violet-700 bg-violet-700 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setVoiceWindow('30')">最近 30 天</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedVoiceWindow === 'all' ? 'border-violet-700 bg-violet-700 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setVoiceWindow('all')">全部</button>
            </div>
          </div>
          <div class="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:min-w-[520px]">
            <div class="rounded-lg border border-white/80 bg-white px-3 py-3">
              <div class="text-xs text-gray-500">Turn 成功率</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">
                {{ voiceTurnSuccessRate }}%
              </div>
            </div>
            <div class="rounded-lg border border-white/80 bg-white px-3 py-3">
              <div class="text-xs text-gray-500">保存转化率</div>
              <div class="mt-1 text-lg font-semibold text-emerald-700">
                {{ voiceFinalizeRate }}%
              </div>
            </div>
            <div class="rounded-lg border border-white/80 bg-white px-3 py-3">
              <div class="text-xs text-gray-500">转写 / 语音失败</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">
                {{ voiceAnalytics.asr_failures }} / {{ voiceAnalytics.tts_failures }}
              </div>
            </div>
            <div class="rounded-lg border border-white/80 bg-white px-3 py-3">
              <div class="text-xs text-gray-500">已完成会话</div>
              <div class="mt-1 text-lg font-semibold text-violet-700">
                {{ voiceAnalytics.finalized_sessions }}
              </div>
            </div>
          </div>
        </div>
      </BaseCard>

      <BaseCard class="mb-8" padding="lg">
        <div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
          <div class="text-center px-4 py-2">
            <div class="text-3xl font-bold text-gray-800">{{ stories.length }}</div>
            <div class="text-gray-500 text-sm mt-1">内容总数</div>
          </div>
          <div class="text-center px-4 py-2">
            <div class="text-3xl font-bold text-gray-800">
              {{ stories.filter((story) => story.mode === 'storybook').length }}
            </div>
            <div class="text-gray-500 text-sm mt-1">绘本数量</div>
          </div>
          <div class="text-center px-4 py-2">
            <div class="text-3xl font-bold text-gray-800">{{ readableCount }}</div>
            <div class="text-gray-500 text-sm mt-1">可阅读</div>
          </div>
          <div class="text-center px-4 py-2">
            <div class="text-3xl font-bold text-gray-800">{{ attentionCount }}</div>
            <div class="text-gray-500 text-sm mt-1">需关注</div>
          </div>
        </div>
      </BaseCard>

      <BaseCard
        v-if="providerAnalytics?.total_calls"
        class="mb-8"
        padding="lg"
      >
        <div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <h2 class="text-xl font-bold text-gray-800">供应商运营摘要</h2>
            <p class="mt-2 text-sm leading-6 text-gray-500">
              最近生成和资源补全留下的供应商调用轨迹。
            </p>
            <div class="mt-4 flex flex-wrap gap-2">
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedWindow === '7' ? 'border-gray-900 bg-gray-900 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setWindow('7')">最近 7 天</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedWindow === '30' ? 'border-gray-900 bg-gray-900 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setWindow('30')">最近 30 天</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedWindow === 'all' ? 'border-gray-900 bg-gray-900 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setWindow('all')">全部</button>
            </div>
            <div class="mt-3 flex flex-wrap gap-2">
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedCapability === 'all' ? 'border-emerald-600 bg-emerald-600 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setCapability('all')">全部能力</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedCapability === 'text' ? 'border-emerald-600 bg-emerald-600 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setCapability('text')">文本</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedCapability === 'image' ? 'border-emerald-600 bg-emerald-600 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setCapability('image')">图片</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedCapability === 'tts' ? 'border-emerald-600 bg-emerald-600 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setCapability('tts')">语音</button>
              <button type="button" class="rounded-lg border px-3 py-1.5 text-sm transition-colors" :class="selectedCapability === 'storybook' ? 'border-emerald-600 bg-emerald-600 text-white' : 'border-gray-200 bg-white text-gray-600 hover:border-gray-400'" @click="setCapability('storybook')">绘本</button>
            </div>
          </div>
          <div class="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:min-w-[520px]">
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">成功率</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ providerSuccessRate }}%</div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">平均耗时</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ formatLatency(providerAnalytics.avg_latency_ms) }}</div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">预估成本</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ formatCost(providerAnalytics.estimated_cost_usd) }}</div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">调用次数</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ providerAnalytics.total_calls }}</div>
            </div>
          </div>
        </div>
        <p v-if="topProvider" class="mt-4 text-sm text-gray-500">
          当前样本中最前面的能力组合是 {{ topProvider.capability }} / {{ topProvider.adapter }}，成功 {{ topProvider.success_count }} 次，失败 {{ topProvider.failure_count }} 次。
        </p>
        <p v-if="topFailureReason" class="mt-2 text-sm text-rose-600">
          最常见失败原因：{{ topFailureReason.reason }}（{{ topFailureReason.count }} 次）
        </p>
      </BaseCard>

      <BaseCard
        v-if="opsSummary"
        class="mb-8"
        padding="lg"
      >
        <div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <h2 class="text-xl font-bold text-gray-800">任务运行概览</h2>
            <p class="mt-2 text-sm leading-6 text-gray-500">
              最近 {{ opsSummary.window_hours }} 小时的任务健康度，运行超过
              {{ opsSummary.stale_threshold_minutes }} 分钟会被视为卡住。
            </p>
          </div>
          <div class="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:min-w-[520px]">
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">运行中</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ opsSummary.active_jobs }}</div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">超时待收敛</div>
              <div class="mt-1 text-lg font-semibold" :class="opsSummary.stale_running_jobs ? 'text-amber-600' : 'text-gray-800'">
                {{ opsSummary.stale_running_jobs }}
              </div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">最近失败</div>
              <div class="mt-1 text-lg font-semibold" :class="opsSummary.failed_jobs ? 'text-rose-600' : 'text-gray-800'">
                {{ opsSummary.failed_jobs }}
              </div>
            </div>
            <div class="rounded-lg border border-gray-100 bg-gray-50 px-3 py-3">
              <div class="text-xs text-gray-500">资源任务</div>
              <div class="mt-1 text-lg font-semibold text-gray-800">{{ opsSummary.asset_retry_jobs }}</div>
            </div>
          </div>
        </div>
        <p v-if="opsSummary.degraded_jobs" class="mt-4 text-sm text-amber-600">
          最近 {{ opsSummary.window_hours }} 小时有 {{ opsSummary.degraded_jobs }} 个任务以降级完成收尾。
        </p>
        <div v-if="opsSummary.recent_failures.length" class="mt-4 space-y-3">
          <div
            v-for="failure in opsSummary.recent_failures"
            :key="failure.job_id"
            class="rounded-lg border border-rose-100 bg-rose-50 px-4 py-3"
          >
            <div class="flex flex-wrap items-center justify-between gap-3">
              <div class="text-sm font-semibold text-gray-800">
                {{ failure.story_title || `${formatOutputMode(failure.output_mode)}任务` }}
              </div>
              <div class="text-xs text-gray-500">{{ formatDate(failure.updated_at) }}</div>
            </div>
            <div class="mt-1 text-xs text-rose-600">
              {{ failure.failure_label }} · {{ failure.error_message || '请打开任务轨迹查看原因' }}
            </div>
          </div>
        </div>
        <p v-else class="mt-4 text-sm text-emerald-600">
          最近 {{ opsSummary.window_hours }} 小时没有失败任务，当前链路比较稳定。
        </p>
      </BaseCard>

      <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
        <router-link
          v-for="story in stories"
          :key="story.id"
          :to="getStoryLink(story)"
          class="block group"
        >
          <BaseCard hover padding="none" class="h-full overflow-hidden flex flex-col">
            <div class="relative aspect-[4/3] overflow-hidden bg-gray-100">
              <img
                v-if="story.image_url"
                :src="story.image_url"
                :alt="story.title"
                class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
              />
              <div
                v-else
                class="w-full h-full flex items-center justify-center text-gray-300"
              >
                <PhotoIcon class="h-12 w-12" />
              </div>

              <div class="absolute top-4 left-4 flex flex-wrap gap-2">
                <span
                  class="px-2.5 py-1 rounded-full text-xs font-medium backdrop-blur-sm"
                  :class="story.mode === 'storybook' ? 'bg-amber-100/90 text-amber-800' : 'bg-violet-100/90 text-violet-800'"
                >
                  {{ story.mode === 'storybook' ? '绘本' : '故事' }}
                </span>
                <span
                  class="px-2.5 py-1 rounded-full text-xs font-medium backdrop-blur-sm"
                  :class="getGenerationStatusMeta(story.generation_status).badgeClass"
                >
                  {{ getGenerationStatusMeta(story.generation_status).label }}
                </span>
              </div>

              <div class="absolute inset-0 bg-black/35 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
                <span class="inline-flex items-center gap-1 px-4 py-2 bg-white/90 text-gray-900 rounded-full font-medium shadow-lg backdrop-blur-sm transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300">
                  {{ story.mode === 'storybook' ? '阅读绘本' : '阅读故事' }}
                  <ChevronRightIcon class="h-4 w-4" />
                </span>
              </div>
            </div>

            <div class="p-5 flex-1 flex flex-col">
              <h3 class="font-bold text-xl text-gray-800 mb-3 line-clamp-2 group-hover:text-purple-600 transition-colors">
                {{ story.title }}
              </h3>

              <p class="text-sm text-gray-500 mb-4 leading-6">
                {{ getGenerationStatusMeta(story.generation_status).description }}
              </p>

              <div class="flex flex-wrap gap-2 mb-4">
                <span
                  class="px-2 py-1 rounded-lg text-xs font-medium"
                  :class="getAssetStatusMeta(story.image_status).badgeClass"
                >
                  封面：{{ getAssetStatusMeta(story.image_status).label }}
                </span>
                <span
                  class="px-2 py-1 rounded-lg text-xs font-medium"
                  :class="getAssetStatusMeta(story.audio_status).badgeClass"
                >
                  音频：{{ getAssetStatusMeta(story.audio_status).label }}
                </span>
              </div>

              <div
                v-if="story.last_error"
                class="mb-4 px-3 py-2 rounded-xl bg-amber-50 text-amber-700 text-sm line-clamp-2"
              >
                {{ story.last_error }}
              </div>

              <div class="mt-auto flex items-center justify-between text-sm text-gray-500">
                <span>{{ formatDate(story.created_at) }}</span>
                <span>
                  {{ story.image_url ? '已有封面' : '待补封面' }}
                </span>
              </div>
            </div>
          </BaseCard>
        </router-link>
      </div>
    </template>

    <CreateStoryModal v-model="showCreateModal" />
  </div>
</template>
