<template>
  <div class="detail-page">
    <div class="detail-head">
      <div>
        <div class="detail-title">任务详情</div>
        <div class="detail-desc">查看当前生成状态、已选附件，以及已完成段落的输出结果。</div>
      </div>
      <div class="detail-actions">
        <a-button v-if="isRunning" danger @click="cancelTask">取消任务</a-button>
        <a-button :disabled="!isCompleted" @click="exportDocx">导出 Word</a-button>
        <a-button :disabled="!isCompleted" @click="exportPdf">导出 PDF</a-button>
      </div>
    </div>

    <div class="stats-grid">
      <div class="stat-card">
        <div class="stat-label">任务状态</div>
        <div class="stat-value">{{ statusText(documentInfo.status) }}</div>
      </div>
      <div class="stat-card">
        <div class="stat-label">完成进度</div>
        <div class="stat-value">{{ documentInfo.para_count_done || 0 }}/{{ documentInfo.para_count_total || 0 }}</div>
      </div>
      <div class="stat-card">
        <div class="stat-label">关联模板</div>
        <div class="stat-value">{{ documentInfo.request_payload?.template_name || documentInfo.name || '-' }}</div>
      </div>
    </div>

    <a-card class="status-card">
      <a-badge :status="statusBadge(documentInfo.status)" :text="statusText(documentInfo.status)" />
      <a-progress style="margin-top: 14px" :percent="progressPercent" />
      <div class="status-message">{{ progressMessage || documentInfo.error || '任务已创建，等待执行。' }}</div>
    </a-card>

    <div class="detail-layout">
      <aside class="detail-left">
        <a-card class="left-card" title="段落列表">
          <div v-if="paragraphMappings.length" class="mapping-list">
            <div
              v-for="item in paragraphMappings"
              :key="item.paragraph_id"
              :class="['mapping-item', { active: selectedParagraphId === item.paragraph_id }]"
              @click="selectParagraph(item.paragraph_id)"
            >
              <div class="mapping-top">
                <span class="mapping-index">{{ item.sort_index }}</span>
                <div class="mapping-main">
                  <div class="mapping-title">{{ item.title }}</div>
                  <div v-if="item.file_note" class="mapping-note">{{ item.file_note }}</div>
                </div>
              </div>
              <div class="mapping-status">
                <a-badge :status="statusBadge(logStatusMap[item.paragraph_id] || 'pending')" :text="statusText(logStatusMap[item.paragraph_id] || 'pending')" />
              </div>
              <div v-if="item.selected_files?.length" class="mapping-files">
                <span v-for="file in item.selected_files" :key="file.file_path" class="mapping-file">{{ file.file_name }}</span>
              </div>
              <div v-else class="mapping-empty">未选择附件</div>
            </div>
          </div>
          <a-empty v-else description="当前任务未记录段落信息" />
        </a-card>
      </aside>

      <section class="detail-right">
        <a-card class="preview-card" title="生成结果预览">
          <template v-if="selectedParagraph">
            <div class="preview-section">
              <div class="preview-section-head">
                <h3>{{ selectedParagraph.title }}</h3>
                <a-badge :status="statusBadge(selectedParagraph.status)" :text="statusText(selectedParagraph.status)" />
              </div>
              <div v-if="selectedMapping?.file_note" class="preview-note">文件要求：{{ selectedMapping.file_note }}</div>
              <div v-if="selectedMapping?.selected_files?.length" class="preview-files">
                <span v-for="file in selectedMapping.selected_files" :key="file.file_path" class="mapping-file">{{ file.file_name }}</span>
              </div>
              <div class="preview-block" v-html="renderBlocks(selectedParagraph.content?.content || [])" />
            </div>
          </template>
          <a-empty v-else :description="isRunning ? '任务进行中，当前段落结果尚未生成' : '当前没有可预览的段落结果'" />
        </a-card>
      </section>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { message } from 'ant-design-vue'
import { useDocumentStore } from '@/stores/document'
import { generateApi } from '@/api/generate'

interface ContentBlock {
  type: string
  text?: string
  title?: string
  headers?: string[]
  rows?: string[][]
}

interface LogItem {
  paragraph_id: number
  title: string
  sort_index: number
  status: string
  content: { content: ContentBlock[] }
}

const route = useRoute()
const docStore = useDocumentStore()
const documentInfo = ref<any>({})
const logs = ref<LogItem[]>([])
const progressPercent = ref(0)
const progressMessage = ref('')
const selectedParagraphId = ref<number | null>(null)
let progressSource: EventSource | null = null

const isRunning = computed(() => ['pending', 'generating'].includes(documentInfo.value.status))
const isCompleted = computed(() => documentInfo.value.status === 'completed')
const paragraphMappings = computed(() => documentInfo.value.request_payload?.paragraphs || [])
const logStatusMap = computed(() =>
  Object.fromEntries(logs.value.map((item) => [item.paragraph_id, item.status]))
)
const selectedParagraph = computed(() =>
  logs.value.find((item) => item.paragraph_id === selectedParagraphId.value) || null
)
const selectedMapping = computed(() =>
  paragraphMappings.value.find((item: any) => item.paragraph_id === selectedParagraphId.value) || null
)

function statusText(status: string) {
  const map: Record<string, string> = {
    completed: '已完成',
    failed: '失败',
    cancelled: '已取消',
    generating: '生成中',
    pending: '等待中',
    success: '成功',
  }
  return map[status] || status || '未知'
}

function statusBadge(status: string) {
  if (status === 'completed' || status === 'success') return 'success'
  if (status === 'failed') return 'error'
  if (status === 'cancelled') return 'warning'
  return 'processing'
}

function renderTable(block: ContentBlock) {
  const headers = block.headers || []
  const rows = block.rows || []
  const thead = headers.length
    ? `<thead><tr>${headers.map((header) => `<th>${header}</th>`).join('')}</tr></thead>`
    : ''
  const tbody = `<tbody>${rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join('')}</tr>`).join('')}</tbody>`
  return `<table class="result-table">${thead}${tbody}</table>`
}

function renderBlocks(blocks: ContentBlock[]) {
  return blocks
    .map((block) => {
      if (block.type === 'table') return renderTable(block)
      return `<p class="result-text">${block.text || ''}</p>`
    })
    .join('')
}

async function loadDocument() {
  const id = Number(route.params.id)
  const response: any = await generateApi.getDocument(id)
  documentInfo.value = response.data || {}
  logs.value = response.data?.logs || []
  if (!selectedParagraphId.value) {
    selectedParagraphId.value = logs.value[0]?.paragraph_id || paragraphMappings.value[0]?.paragraph_id || null
  }
  if (
    selectedParagraphId.value &&
    !paragraphMappings.value.some((item: any) => item.paragraph_id === selectedParagraphId.value) &&
    !logs.value.some((item) => item.paragraph_id === selectedParagraphId.value)
  ) {
    selectedParagraphId.value = logs.value[0]?.paragraph_id || paragraphMappings.value[0]?.paragraph_id || null
  }
}

function selectParagraph(paragraphId: number) {
  selectedParagraphId.value = paragraphId
}

function bindProgress() {
  const id = Number(route.params.id)
  if (progressSource) progressSource.close()
  progressSource = new EventSource(generateApi.progress(id))
  progressSource.addEventListener('progress', async (event: MessageEvent) => {
    const payload = JSON.parse(event.data)
    progressPercent.value = payload.percent || 0
    progressMessage.value = payload.message || ''
    await loadDocument()
    if (['completed', 'failed', 'cancelled'].includes(payload.status)) {
      progressSource?.close()
      progressSource = null
    }
  })
  progressSource.onerror = () => {
    progressSource?.close()
    progressSource = null
  }
}

async function cancelTask() {
  const id = Number(route.params.id)
  await docStore.cancel(id)
  message.info('已发起取消请求')
}

function exportDocx() {
  window.open(generateApi.exportDocx(Number(route.params.id)))
}

function exportPdf() {
  window.open(generateApi.exportPdf(Number(route.params.id)))
}

onMounted(async () => {
  try {
    await loadDocument()
    progressPercent.value = Math.floor(((documentInfo.value.para_count_done || 0) / Math.max(documentInfo.value.para_count_total || 1, 1)) * 100)
    if (isRunning.value) bindProgress()
  } catch (error: any) {
    message.error(error.message || '加载任务详情失败')
  }
})

onBeforeUnmount(() => {
  progressSource?.close()
  progressSource = null
})
</script>

<style scoped>
.detail-page {
  padding: 24px;
  height: calc(100vh - 52px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

.detail-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 20px;
}

.detail-title {
  font-size: 24px;
  font-weight: 700;
  color: #111827;
}

.detail-desc {
  margin-top: 6px;
  font-size: 13px;
  color: #6b7280;
}

.detail-actions {
  display: flex;
  gap: 8px;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}

.stat-card {
  padding: 18px;
  border-radius: 16px;
  border: 1px solid #e5e7eb;
  background: linear-gradient(135deg, #ffffff 0%, #f7f8fc 100%);
}

.stat-label {
  font-size: 13px;
  color: #6b7280;
}

.stat-value {
  margin-top: 8px;
  font-size: 22px;
  font-weight: 700;
  color: #111827;
}

.status-card,
.preview-card {
  margin-bottom: 16px;
  border-radius: 18px;
  flex-shrink: 0;
}

.detail-layout {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  min-height: 0;
  flex: 1;
  overflow: hidden;
}

.detail-left {
  width: 360px;
  flex-shrink: 0;
  height: 100%;
  min-height: 0;
}

.detail-right {
  min-width: 0;
  flex: 1;
  height: 100%;
  min-height: 0;
}

.left-card {
  border-radius: 18px;
  height: 100%;
}

.status-message {
  margin-top: 12px;
  font-size: 13px;
  color: #6b7280;
}

.mapping-list {
  display: grid;
  gap: 12px;
}

.mapping-item {
  padding: 14px;
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  background: #fafbfc;
  cursor: pointer;
  transition: all 0.15s ease;
}

.mapping-item:hover {
  border-color: #c7d2fe;
  background: #f8faff;
}

.mapping-item.active {
  border-color: #4f46e5;
  background: #eef2ff;
}

.mapping-top {
  display: flex;
  gap: 10px;
}

.mapping-index {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #eef2ff;
  color: #4f46e5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
}

.mapping-main {
  flex: 1;
}

.mapping-title {
  font-size: 14px;
  font-weight: 600;
  color: #111827;
}

.mapping-note {
  margin-top: 4px;
  font-size: 12px;
  color: #6b7280;
}

.mapping-status {
  margin-top: 10px;
}

.mapping-files {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.mapping-file {
  padding: 4px 10px;
  border-radius: 999px;
  background: #eefbf2;
  border: 1px solid #cdebd8;
  color: #1a8c4a;
  font-size: 12px;
}

.mapping-empty {
  margin-top: 10px;
  font-size: 12px;
  color: #9ca3af;
}

.preview-section {
  padding: 16px;
  border-radius: 14px;
  background: #fff;
  border: 1px solid #e5e7eb;
  min-height: 100%;
}

.preview-section-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  margin-bottom: 12px;
}

.preview-section-head h3 {
  margin: 0;
}

.preview-note {
  margin-bottom: 12px;
  font-size: 13px;
  color: #6b7280;
}

.preview-files {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}

:deep(.left-card .ant-card-body) {
  height: calc(100% - 57px);
  overflow-y: auto;
}

:deep(.preview-card) {
  height: 100%;
}

:deep(.preview-card .ant-card-body) {
  height: calc(100% - 57px);
  overflow-y: auto;
}

:deep(.result-text) {
  margin: 0 0 12px;
  line-height: 1.8;
}

:deep(.result-table) {
  width: 100%;
  border-collapse: collapse;
}

:deep(.result-table th),
:deep(.result-table td) {
  border: 1px solid #d1d5db;
  padding: 6px 8px;
  font-size: 12px;
}
</style>
