<template>
  <article class="knowledge-ingest-panel panel">
    <header class="ingest-head">
      <div>
        <span class="eyebrow">LightRAG 知识归集</span>
        <h3>{{ model.folder || '未指定目录' }}</h3>
        <p>{{ model.phaseLabel }} · {{ model.statusLabel }}</p>
      </div>
      <div class="progress-ring" :aria-label="`归集进度 ${model.progress.percent}%`">
        <strong>{{ model.progress.percent }}%</strong>
        <span>进度</span>
      </div>
    </header>

    <div class="progress-bar" aria-hidden="true">
      <span :style="{ width: `${model.progress.percent}%` }"></span>
    </div>

    <div class="metric-strip">
      <div v-for="metric in model.metrics" :key="metric.label" class="metric-tile">
        <span>{{ metric.label }}</span>
        <strong>{{ metric.value }}</strong>
        <small>{{ metric.hint }}</small>
      </div>
    </div>

    <div class="ingest-workspace">
      <aside class="file-rail">
        <button
          v-for="document in model.documents"
          :key="document.documentId"
          type="button"
          class="file-item"
          :class="{ active: selectedDocumentId === document.documentId }"
          @click="selectDocument(document.documentId)"
        >
          <i :class="documentIcon(document)"></i>
          <span class="file-copy">
            <strong>{{ document.name }}</strong>
            <small>
              {{ document.phaseLabel }} · {{ document.chunkCount }} chunk
            </small>
          </span>
          <span class="mini-status" :class="document.statusTone">
            {{ document.statusLabel }}
          </span>
        </button>
      </aside>

      <section v-if="selectedDocument" class="file-detail">
        <div class="detail-topline">
          <div>
            <h4>{{ selectedDocument.name }}</h4>
            <p>
              {{ selectedDocument.folder || '根目录' }}
              <span v-if="selectedDocument.extension"> · {{ selectedDocument.extension }}</span>
            </p>
          </div>
          <span class="status-chip" :class="selectedDocument.statusTone">
            {{ selectedDocument.statusLabel }}
          </span>
        </div>

        <div class="detail-stats">
          <div>
            <span>原文字符</span>
            <strong>{{ formatKnowledgeMetric(selectedDocument.textChars) }}</strong>
          </div>
          <div>
            <span>索引字符</span>
            <strong>{{ formatKnowledgeMetric(selectedDocument.indexedTextChars) }}</strong>
          </div>
          <div>
            <span>Chunk</span>
            <strong>{{ formatKnowledgeMetric(selectedDocument.chunkCount) }}</strong>
          </div>
          <div>
            <span>实体 / 关系</span>
            <strong>
              {{ formatKnowledgeMetric(selectedDocument.entityCount) }}
              /
              {{ formatKnowledgeMetric(selectedDocument.relationCount) }}
            </strong>
          </div>
        </div>

        <p v-if="selectedDocument.error" class="error-note">
          {{ selectedDocument.error }}
        </p>

        <div class="detail-section-grid">
          <section class="detail-section">
            <div class="section-head">
              <h5>Chunk 信息</h5>
              <span>{{ selectedDocument.chunks.length }} 条</span>
            </div>
            <div v-if="selectedDocument.chunks.length" class="chunk-list">
              <div v-for="chunk in selectedDocument.chunks" :key="chunk.id" class="chunk-row">
                <span class="chunk-index">#{{ chunk.order + 1 }}</span>
                <div>
                  <strong>{{ compactId(chunk.id) }}</strong>
                  <p>{{ chunk.summary || '暂无摘要' }}</p>
                </div>
                <small>{{ chunk.tokens }} tokens</small>
              </div>
            </div>
            <div v-else class="compact-empty">暂无 chunk 明细</div>
          </section>

          <section class="detail-section">
            <div class="section-head">
              <h5>章节提取</h5>
              <span>{{ selectedDocument.sectionCount }} 条</span>
            </div>
            <div v-if="selectedDocument.sections.length" class="section-list">
              <div
                v-for="section in selectedDocument.sections"
                :key="section.title"
                class="section-row"
              >
                <strong>{{ section.title }}</strong>
                <p>{{ section.excerpt || '暂无章节摘要' }}</p>
              </div>
            </div>
            <div v-else class="compact-empty">暂无章节信息</div>
          </section>
        </div>

        <section class="detail-section">
          <div class="section-head">
            <h5>处理事件</h5>
            <span>{{ selectedDocument.events.length }} 条</span>
          </div>
          <div v-if="selectedDocument.events.length" class="event-list">
            <div
              v-for="event in selectedDocument.events"
              :key="`${event.at}-${event.message}`"
              class="event-row"
              :class="event.level"
            >
              <span></span>
              <div>
                <strong>{{ formatEventTime(event.at) }}</strong>
                <p>{{ event.message }}</p>
              </div>
            </div>
          </div>
          <div v-else class="compact-empty">暂无处理事件</div>
        </section>
      </section>
    </div>

    <section class="graph-section">
      <div class="section-head">
        <h4>图谱形成</h4>
        <span>
          {{ formatKnowledgeMetric(model.graph.entityCount) }} 实体 ·
          {{ formatKnowledgeMetric(model.graph.relationCount) }} 关系
        </span>
      </div>
      <div class="graph-grid">
        <div class="graph-pane">
          <h5>实体</h5>
          <div v-if="model.graph.entities.length" class="entity-cloud">
            <span v-for="entity in model.graph.entities" :key="entity">{{ entity }}</span>
          </div>
          <div v-else class="compact-empty">暂无实体</div>
        </div>
        <div class="graph-pane">
          <h5>关系</h5>
          <div v-if="model.graph.relations.length" class="relation-list">
            <div
              v-for="relation in model.graph.relations"
              :key="`${relation.source}-${relation.target}-${relation.type}`"
              class="relation-row"
            >
              <strong>{{ relation.source }}</strong>
              <i class="mdi mdi-arrow-right-thin"></i>
              <strong>{{ relation.target }}</strong>
              <span>{{ relation.type }}</span>
            </div>
          </div>
          <div v-else class="compact-empty">暂无关系</div>
        </div>
      </div>
    </section>
  </article>
</template>

<script setup>
import { computed, ref, watch } from 'vue'

import {
  buildKnowledgeIngestLogModel,
  formatKnowledgeMetric
} from '../../utils/knowledgeIngestLogModel.js'

const props = defineProps({
  run: {
    type: Object,
    required: true
  }
})

const selectedDocumentId = ref('')
const model = computed(() => buildKnowledgeIngestLogModel(props.run))
const selectedDocument = computed(
  () => model.value.documents.find((item) => item.documentId === selectedDocumentId.value) || null
)

watch(
  () => model.value.selectedDocumentId,
  (nextDocumentId) => {
    if (!nextDocumentId) {
      selectedDocumentId.value = ''
      return
    }
    if (!selectedDocumentId.value || !model.value.documents.some((item) => item.documentId === selectedDocumentId.value)) {
      selectedDocumentId.value = nextDocumentId
    }
  },
  { immediate: true }
)

function selectDocument(documentId) {
  selectedDocumentId.value = documentId
}

function documentIcon(document) {
  const extension = String(document?.extension || '').toLowerCase()
  if (extension === 'pdf') return 'mdi mdi-file-pdf-box'
  if (['doc', 'docx'].includes(extension)) return 'mdi mdi-file-word-box'
  if (['xls', 'xlsx', 'csv'].includes(extension)) return 'mdi mdi-file-excel-box'
  if (['ppt', 'pptx'].includes(extension)) return 'mdi mdi-file-powerpoint-box'
  return 'mdi mdi-file-document-outline'
}

function compactId(value) {
  const text = String(value || '').trim()
  if (text.length <= 18) return text || 'chunk'
  return `${text.slice(0, 8)}...${text.slice(-6)}`
}

function formatEventTime(value) {
  if (!value) return '刚刚'
  const date = new Date(value)
  if (Number.isNaN(date.getTime())) return String(value)
  return date.toLocaleTimeString('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  })
}
</script>

<style scoped>
.knowledge-ingest-panel {
  display: grid;
  gap: 14px;
  padding: 18px;
}

.ingest-head {
  display: flex;
  justify-content: space-between;
  gap: 18px;
}

.eyebrow {
  color: #0f766e;
  font-size: 12px;
  font-weight: 800;
}

.ingest-head h3 {
  margin: 5px 0 0;
  color: #0f172a;
  font-size: 18px;
}

.ingest-head p {
  margin: 6px 0 0;
  color: #64748b;
  font-size: 13px;
}

.progress-ring {
  width: 72px;
  height: 72px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border: 1px solid #d7e0ea;
  border-radius: 50%;
  background: #f8fafc;
}

.progress-ring strong,
.progress-ring span {
  grid-area: 1 / 1;
}

.progress-ring strong {
  margin-top: -12px;
  color: #0f172a;
  font-size: 17px;
}

.progress-ring span {
  margin-top: 26px;
  color: #64748b;
  font-size: 11px;
}

.progress-bar {
  height: 7px;
  overflow: hidden;
  border-radius: 999px;
  background: #e5eaf0;
}

.progress-bar span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #0f766e, #2563eb);
  transition: width 0.24s ease;
}

.metric-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.metric-tile {
  min-width: 0;
  display: grid;
  gap: 4px;
  padding: 11px 12px;
  border: 1px solid #e5edf5;
  border-radius: 8px;
  background: #fff;
}

.metric-tile span,
.metric-tile small {
  color: #64748b;
  font-size: 12px;
}

.metric-tile strong {
  color: #0f172a;
  font-size: 18px;
  line-height: 1.2;
}

.ingest-workspace {
  display: grid;
  grid-template-columns: minmax(230px, 0.85fr) minmax(0, 2fr);
  gap: 14px;
  min-height: 360px;
}

.file-rail {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 8px;
}

.file-item {
  width: 100%;
  min-height: 58px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid #e5edf5;
  border-radius: 8px;
  background: #fff;
  text-align: left;
}

.file-item.active {
  border-color: rgba(15, 118, 110, 0.38);
  background: #f0fdfa;
  box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.08);
}

.file-item > i {
  color: #334155;
  font-size: 24px;
}

.file-copy {
  min-width: 0;
  display: grid;
  gap: 3px;
}

.file-copy strong,
.file-copy small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-copy strong {
  color: #0f172a;
  font-size: 13px;
}

.file-copy small {
  color: #64748b;
  font-size: 12px;
}

.mini-status,
.status-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
}

.mini-status.success,
.status-chip.success {
  background: #dcfce7;
  color: #166534;
}

.mini-status.warning,
.status-chip.warning {
  background: #fef3c7;
  color: #92400e;
}

.mini-status.danger,
.status-chip.danger {
  background: #fee2e2;
  color: #991b1b;
}

.mini-status.muted,
.status-chip.muted {
  background: #eef2f7;
  color: #475569;
}

.file-detail {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 12px;
}

.detail-topline {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid #e5edf5;
}

.detail-topline h4 {
  margin: 0;
  color: #0f172a;
  font-size: 16px;
}

.detail-topline p {
  margin: 5px 0 0;
  color: #64748b;
  font-size: 12px;
}

.detail-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.detail-stats div {
  min-width: 0;
  display: grid;
  gap: 4px;
  padding: 10px;
  border-radius: 8px;
  background: #f8fafc;
}

.detail-stats span {
  color: #64748b;
  font-size: 12px;
}

.detail-stats strong {
  color: #0f172a;
  font-size: 13px;
}

.error-note {
  margin: 0;
  padding: 10px 12px;
  border: 1px solid #fecaca;
  border-radius: 8px;
  background: #fff1f2;
  color: #991b1b;
  font-size: 13px;
}

.detail-section-grid,
.graph-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.detail-section,
.graph-section,
.graph-pane {
  min-width: 0;
  display: grid;
  gap: 10px;
  align-content: start;
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.section-head h4,
.section-head h5,
.graph-pane h5 {
  margin: 0;
  color: #0f172a;
  font-size: 14px;
}

.section-head span {
  color: #64748b;
  font-size: 12px;
  font-weight: 700;
}

.chunk-list,
.section-list,
.event-list,
.relation-list {
  display: grid;
  gap: 8px;
}

.chunk-row,
.section-row,
.event-row,
.relation-row {
  min-width: 0;
  border: 1px solid #e5edf5;
  border-radius: 8px;
  background: #fff;
}

.chunk-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 10px;
  padding: 10px;
}

.chunk-index {
  color: #2563eb;
  font-size: 12px;
  font-weight: 850;
}

.chunk-row strong,
.section-row strong,
.event-row strong,
.relation-row strong {
  color: #0f172a;
  font-size: 12px;
}

.chunk-row p,
.section-row p,
.event-row p {
  margin: 4px 0 0;
  color: #64748b;
  font-size: 12px;
  line-height: 1.55;
}

.chunk-row small {
  color: #64748b;
  font-size: 11px;
  white-space: nowrap;
}

.section-row {
  padding: 10px;
}

.event-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  padding: 10px;
}

.event-row > span {
  width: 8px;
  height: 8px;
  margin-top: 5px;
  border-radius: 999px;
  background: #2563eb;
}

.event-row.error > span {
  background: #dc2626;
}

.entity-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.entity-cloud span {
  max-width: 100%;
  padding: 5px 9px;
  border: 1px solid #bfdbfe;
  border-radius: 999px;
  background: #eff6ff;
  color: #1d4ed8;
  font-size: 12px;
  font-weight: 750;
}

.relation-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: 9px 10px;
}

.relation-row strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.relation-row i {
  color: #0f766e;
  font-size: 18px;
}

.relation-row span {
  padding: 3px 7px;
  border-radius: 999px;
  background: #f1f5f9;
  color: #475569;
  font-size: 11px;
}

.compact-empty {
  min-height: 42px;
  display: grid;
  place-items: center;
  border: 1px dashed #cbd5e1;
  border-radius: 8px;
  color: #64748b;
  font-size: 12px;
}

@media (max-width: 980px) {
  .metric-strip,
  .detail-stats,
  .detail-section-grid,
  .graph-grid,
  .ingest-workspace {
    grid-template-columns: 1fr;
  }

  .file-rail {
    max-height: 260px;
    overflow: auto;
  }
}

@media (max-width: 620px) {
  .ingest-head,
  .detail-topline {
    flex-direction: column;
  }

  .progress-ring {
    width: 64px;
    height: 64px;
  }

  .file-item {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .mini-status {
    grid-column: 2;
    justify-self: start;
  }

  .relation-row {
    grid-template-columns: 1fr;
  }
}
</style>
