<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { BoltIcon, ClockIcon } from '@heroicons/vue/24/outline'
import { api } from '../api/client'
import LoadingSpinner from './ui/LoadingSpinner.vue'

interface GenerationJobSummary {
  id: string
  story_id: number | null
  output_mode: string
  input_type: string
  status: string
  current_step: string
  progress_percent: number
  progress_label: string
  is_terminal: boolean
  result_snapshot: Record<string, unknown>
  error_message: string | null
  created_at: string
  updated_at: string
}

interface GenerationJobEvent {
  id: number
  job_id: string
  story_id: number | null
  event_type: string
  status: string
  message: string | null
  event_metadata: Record<string, unknown>
  created_at: string
}

interface GenerationJobDetail extends GenerationJobSummary {
  request_payload: Record<string, unknown>
  events: GenerationJobEvent[]
}

interface GenerationProviderStats {
  story_id: number
  total_calls: number
  successful_calls: number
  failed_calls: number
  avg_latency_ms: number | null
  estimated_cost_usd: number
}

const props = withDefaults(
  defineProps<{
    storyId: number | null
    tone?: 'light' | 'dark'
    title?: string
    description?: string
  }>(),
  {
    tone: 'light',
    title: '生成轨迹',
    description: '查看生成、资源补全和 Provider 调用事件，便于演示时解释状态来源与失败恢复。',
  },
)

const jobs = ref<GenerationJobSummary[]>([])
const activeJob = ref<GenerationJobDetail | null>(null)
const providerStats = ref<GenerationProviderStats | null>(null)
const loading = ref(false)
const error = ref('')
let refreshTimer: ReturnType<typeof setInterval> | null = null

const isDark = computed(() => props.tone === 'dark')
const latestJob = computed(() => jobs.value[0] ?? null)
const activeEvents = computed(() => activeJob.value?.events.slice(-10) ?? [])
const activeProgress = computed(() => activeJob.value?.progress_percent ?? latestJob.value?.progress_percent ?? 0)
const activeProgressLabel = computed(() => activeJob.value?.progress_label ?? latestJob.value?.progress_label ?? '暂无进度')
const shouldAutoRefresh = computed(() => {
  if (activeJob.value) return !activeJob.value.is_terminal
  if (latestJob.value) return !latestJob.value.is_terminal
  return false
})
const providerSuccessRate = computed(() => {
  if (!providerStats.value?.total_calls) return null
  return Math.round((providerStats.value.successful_calls / providerStats.value.total_calls) * 100)
})
const mutedClass = computed(() => (isDark.value ? 'text-white/65' : 'text-gray-500'))
const shellClass = computed(() => (
  isDark.value ? 'border-white/10 bg-white/10 text-white backdrop-blur' : 'border-gray-100 bg-white/85 text-gray-900'
))
const panelClass = computed(() => (
  isDark.value ? 'border-white/10 bg-black/25' : 'border-gray-100 bg-gray-50/80'
))

const statusClassMap: Record<string, string> = {
  running: 'border-amber-200 bg-amber-50 text-amber-700',
  queued: 'border-sky-200 bg-sky-50 text-sky-700',
  succeeded: 'border-emerald-200 bg-emerald-50 text-emerald-700',
  completed: 'border-emerald-200 bg-emerald-50 text-emerald-700',
  degraded_completed: 'border-orange-200 bg-orange-50 text-orange-700',
  failed: 'border-rose-200 bg-rose-50 text-rose-700',
}

function statusClass(status?: string) {
  return statusClassMap[status ?? ''] ?? 'border-gray-200 bg-gray-50 text-gray-600'
}

function statusLabel(status?: string) {
  const labels: Record<string, string> = {
    running: '进行中',
    queued: '已排队',
    succeeded: '成功',
    completed: '已完成',
    degraded_completed: '降级完成',
    failed: '失败',
  }
  return labels[status ?? ''] ?? '未知'
}

function eventLabel(eventType: string) {
  const labels: Record<string, string> = {
    request_accepted: '请求接收',
    worker_started: '后台任务开始',
    context_prepared: '上下文准备',
    narrative_generated: '正文生成',
    story_saved: '故事保存',
    provider_call_started: 'Provider 调用',
    provider_call_succeeded: 'Provider 成功',
    provider_call_failed: 'Provider 失败',
    cover_image_started: '封面开始',
    cover_image_succeeded: '封面就绪',
    cover_image_failed: '封面失败',
    storybook_images_started: '绘本插图开始',
    storybook_images_completed: '绘本插图完成',
    storybook_page_image_succeeded: '分页插图就绪',
    storybook_page_image_failed: '分页插图失败',
    audio_cache_hit: '音频缓存命中',
    audio_started: '音频开始',
    audio_succeeded: '音频就绪',
    audio_failed: '音频失败',
    asset_retry_started: '资源重试开始',
    asset_retry_completed: '资源重试完成',
    asset_retry_failed: '资源重试失败',
    generation_completed: '生成完成',
    generation_failed: '生成失败',
  }
  return labels[eventType] ?? eventType
}

function formatTime(value: string) {
  return new Intl.DateTimeFormat('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  }).format(new Date(value))
}

function providerText(event: GenerationJobEvent) {
  const meta = event.event_metadata
  const adapter = meta.adapter ? String(meta.adapter) : ''
  const latency = typeof meta.latency_ms === 'number' ? `${meta.latency_ms}ms` : ''
  const cost = typeof meta.estimated_cost_usd === 'number'
    ? `$${meta.estimated_cost_usd.toFixed(4)}`
    : ''
  return [adapter, latency, cost].filter(Boolean).join(' · ')
}

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'
}

async function selectJob(jobId: string) {
  loading.value = true
  error.value = ''

  try {
    activeJob.value = await api.get<GenerationJobDetail>(`/api/generations/jobs/${jobId}`)
  } catch (e) {
    error.value = e instanceof Error ? e.message : '生成轨迹加载失败'
  } finally {
    loading.value = false
  }
}

async function refresh() {
  if (props.storyId === null) {
    jobs.value = []
    activeJob.value = null
    providerStats.value = null
    return
  }

  error.value = ''

  try {
    const [nextJobs, stats] = await Promise.all([
      api.get<GenerationJobSummary[]>(`/api/generations/${props.storyId}/jobs`),
      api.get<GenerationProviderStats>(`/api/generations/${props.storyId}/provider-stats`),
    ])
    jobs.value = nextJobs
    providerStats.value = stats
    const nextJobId = jobs.value[0]?.id
    if (nextJobId) {
      await selectJob(nextJobId)
    } else {
      activeJob.value = null
    }
  } catch (e) {
    jobs.value = []
    activeJob.value = null
    providerStats.value = null
    error.value = e instanceof Error ? e.message : '生成轨迹加载失败'
  }
}

function stopAutoRefresh() {
  if (refreshTimer) {
    clearInterval(refreshTimer)
    refreshTimer = null
  }
}

watch(
  () => props.storyId,
  () => {
    void refresh()
  },
  { immediate: true },
)

watch(shouldAutoRefresh, (enabled) => {
  stopAutoRefresh()
  if (enabled) {
    refreshTimer = setInterval(() => {
      if (!loading.value) {
        void refresh()
      }
    }, 2500)
  }
})

onBeforeUnmount(stopAutoRefresh)

defineExpose({ refresh })
</script>

<template>
  <section class="rounded-lg border p-5" :class="shellClass">
    <div class="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
      <div>
        <div class="flex items-center gap-2 font-semibold">
          <BoltIcon class="h-5 w-5 text-amber-500" />
          {{ props.title }}
        </div>
        <p class="mt-2 text-sm leading-6" :class="mutedClass">
          {{ props.description }}
        </p>
      </div>
      <span
        v-if="latestJob"
        class="w-fit rounded-full border px-3 py-1.5 text-sm font-medium"
        :class="statusClass(latestJob.status)"
      >
        最近任务：{{ statusLabel(latestJob.status) }}
      </span>
    </div>

    <div v-if="error" class="mt-4 rounded-lg border border-rose-100 bg-rose-50 p-3 text-sm text-rose-600">
      {{ error }}
    </div>

    <div v-else class="mt-5 space-y-5">
      <div
        v-if="providerStats?.total_calls"
        class="grid gap-3 md:grid-cols-4"
      >
        <div class="rounded-lg border p-3" :class="panelClass">
          <div class="text-xs" :class="mutedClass">Provider 成功率</div>
          <div class="mt-1 text-xl font-semibold">{{ providerSuccessRate }}%</div>
        </div>
        <div class="rounded-lg border p-3" :class="panelClass">
          <div class="text-xs" :class="mutedClass">平均耗时</div>
          <div class="mt-1 text-xl font-semibold">{{ formatLatency(providerStats.avg_latency_ms) }}</div>
        </div>
        <div class="rounded-lg border p-3" :class="panelClass">
          <div class="text-xs" :class="mutedClass">预估成本</div>
          <div class="mt-1 text-xl font-semibold">{{ formatCost(providerStats.estimated_cost_usd) }}</div>
        </div>
        <div class="rounded-lg border p-3" :class="panelClass">
          <div class="text-xs" :class="mutedClass">调用次数</div>
          <div class="mt-1 text-xl font-semibold">{{ providerStats.total_calls }}</div>
        </div>
      </div>

      <div v-if="!jobs.length" class="rounded-lg border border-dashed border-gray-200 p-4 text-sm" :class="mutedClass">
        暂无生成轨迹。旧数据会在下一次资源补全后开始记录。
      </div>

      <div v-else class="grid gap-5 lg:grid-cols-[220px_minmax(0,1fr)]">
      <div class="flex gap-2 overflow-x-auto pb-1 lg:block lg:space-y-2 lg:overflow-visible">
        <button
          v-for="job in jobs"
          :key="job.id"
          type="button"
          class="min-w-[190px] rounded-lg border px-3 py-3 text-left transition lg:w-full"
          :class="activeJob?.id === job.id ? 'border-gray-900 bg-gray-50 text-gray-900' : 'border-gray-100 bg-white text-gray-900 hover:bg-gray-50'"
          @click="selectJob(job.id)"
        >
          <div class="flex items-center justify-between gap-2">
            <span class="text-sm font-semibold">
              {{ job.output_mode === 'asset_retry' ? '资源重试' : '内容生成' }}
            </span>
            <span class="rounded-full border px-2 py-0.5 text-xs" :class="statusClass(job.status)">
              {{ statusLabel(job.status) }}
            </span>
          </div>
          <div class="mt-2 flex items-center gap-1 text-xs text-gray-500">
            <ClockIcon class="h-4 w-4" />
            {{ formatTime(job.created_at) }}
          </div>
        </button>
      </div>

      <div class="rounded-lg border p-4" :class="panelClass">
        <LoadingSpinner v-if="loading" size="sm" text="正在读取生成轨迹..." />

        <div v-else-if="activeJob" class="space-y-3">
          <div class="flex flex-wrap items-center justify-between gap-3">
            <div>
              <div class="text-sm font-semibold">
                {{ activeJob.output_mode === 'asset_retry' ? '资源重试事件' : '生成事件' }}
              </div>
              <div class="mt-1 text-xs" :class="mutedClass">
                当前步骤：{{ eventLabel(activeJob.current_step) }}
              </div>
            </div>
            <span class="rounded-full border px-3 py-1 text-xs font-medium" :class="statusClass(activeJob.status)">
              {{ statusLabel(activeJob.status) }}
            </span>
          </div>

          <div>
            <div class="mb-1 flex items-center justify-between text-xs" :class="mutedClass">
              <span>{{ activeProgressLabel }}</span>
              <span>{{ activeProgress }}%</span>
            </div>
            <div class="h-2 overflow-hidden rounded-full bg-gray-200/80">
              <div
                class="h-full rounded-full bg-emerald-500 transition-all duration-300"
                :style="{ width: `${activeProgress}%` }"
              />
            </div>
          </div>

          <ol class="space-y-3">
            <li
              v-for="event in activeEvents"
              :key="event.id"
              class="grid grid-cols-[88px_minmax(0,1fr)] gap-3"
            >
              <div class="pt-1 text-xs" :class="mutedClass">
                {{ formatTime(event.created_at) }}
              </div>
              <div class="rounded-lg border border-white bg-white px-3 py-2 text-gray-900">
                <div class="flex flex-wrap items-center gap-2">
                  <span class="text-sm font-semibold">{{ eventLabel(event.event_type) }}</span>
                  <span class="rounded-full border px-2 py-0.5 text-xs" :class="statusClass(event.status)">
                    {{ statusLabel(event.status) }}
                  </span>
                </div>
                <p v-if="event.event_type.startsWith('provider_call')" class="mt-1 text-xs text-gray-500">
                  {{ providerText(event) }}
                </p>
                <p v-else-if="event.message" class="mt-1 text-xs text-gray-500">
                  {{ event.message }}
                </p>
              </div>
            </li>
          </ol>
        </div>
      </div>
    </div>
    </div>
  </section>
</template>
