<template>
  <div class="knowledge-workbench fill-height">
    <header class="workbench-header">
      <div>
        <div class="d-flex align-center mb-2">
          <v-icon color="primary" class="mr-2">mdi-book-search-outline</v-icon>
          <span class="text-overline primary--text">DATAOPS KNOWLEDGE</span>
        </div>
        <h1 class="text-h4 font-weight-bold mb-2">数据治理知识库</h1>
        <p class="text-body-1 grey--text text--darken-1 mb-0">
          基于当前有效治理事实进行检索与问答，每条结论都能核验来源版本和知识点。
        </p>
      </div>
      <div class="status-cluster">
        <v-chip small outlined color="success">
          <v-icon left small>mdi-database-check-outline</v-icon>
          标准检索可用
        </v-chip>
        <v-chip v-if="capabilities.lightrag_shadow_only" small outlined color="primary">
          LightRAG 影子模式
        </v-chip>
      </div>
    </header>

    <main class="workbench-grid">
      <section class="query-panel">
        <div class="mode-switch mb-5">
          <v-btn-toggle v-model="action" mandatory dense color="primary">
            <v-btn value="ask">问答</v-btn>
            <v-btn value="search">仅检索</v-btn>
          </v-btn-toggle>
          <v-select
            v-model="mode"
            :items="modes"
            item-text="label"
            item-value="value"
            label="检索模式"
            dense
            outlined
            hide-details
            class="mode-select"
          />
        </div>

        <label class="text-subtitle-2 d-block mb-2" for="knowledge-query">治理问题</label>
        <v-textarea
          id="knowledge-query"
          v-model="query"
          outlined
          auto-grow
          rows="3"
          :placeholder="action === 'ask' ? '例如：客户同步流程由谁负责？它依赖哪些上游数据？' : '输入对象名、字段名、编码或治理概念'"
          :error-messages="queryError"
          @keydown.ctrl.enter="submit"
        />
        <div class="d-flex align-center justify-space-between">
          <span class="text-caption grey--text">Ctrl + Enter 提交；业务域范围由服务端授权决定</span>
          <v-btn color="primary" depressed :loading="loading" :disabled="!query.trim()" @click="submit">
            <v-icon left>mdi-arrow-right</v-icon>
            {{ action === 'ask' ? '生成回答' : '开始检索' }}
          </v-btn>
        </div>

        <v-alert v-if="error" type="error" outlined dense class="mt-5 mb-0">
          {{ error }}
        </v-alert>
        <v-alert v-if="degradedComponents.length" type="warning" outlined dense class="mt-5 mb-0">
          部分能力暂时降级：{{ degradedComponents.join('、') }}。当前结果仅使用可用检索器生成。
        </v-alert>

        <div v-if="loading" class="result-skeleton mt-8" aria-label="正在检索">
          <v-skeleton-loader type="heading, paragraph, list-item-three-line, list-item-three-line" />
        </div>

        <div v-else-if="hasResult" class="result-area mt-8">
          <div v-if="action === 'ask'" class="answer-block">
            <div class="d-flex align-center justify-space-between mb-3">
              <h2 class="text-h6 mb-0">回答</h2>
              <v-chip small :color="freshnessColor" outlined>{{ freshnessLabel }}</v-chip>
            </div>
            <p v-if="answer" class="answer-copy">{{ answer }}</p>
            <v-alert v-else type="info" outlined dense class="mb-0">
              当前有效证据不足，知识库没有生成推测性答案。你仍可查看下方检索来源。
            </v-alert>
          </div>

          <div class="evidence-header">
            <h2 class="text-h6 mb-0">{{ action === 'ask' ? '引用来源' : '检索结果' }}</h2>
            <span class="text-caption grey--text">{{ evidence.length }} 条</span>
          </div>
          <div v-if="evidence.length" class="evidence-list">
            <button
              v-for="(item, index) in evidence"
              :key="`${item.chunk_id}-${index}`"
              type="button"
              class="evidence-row"
              @click="openSource(item)"
            >
              <span class="evidence-index">{{ String(index + 1).padStart(2, '0') }}</span>
              <span class="evidence-main">
                <span class="d-flex align-center flex-wrap mb-2">
                  <strong class="mr-2">{{ item.object_type }}</strong>
                  <v-chip x-small outlined class="mr-2">v{{ item.object_version }}</v-chip>
                  <v-chip x-small outlined color="primary">point r{{ firstRevision(item) }}</v-chip>
                </span>
                <span class="evidence-content">{{ item.content }}</span>
                <span class="evidence-meta mt-2">
                  {{ firstPoint(item) }} · generation {{ item.index_generation }}
                </span>
              </span>
              <v-icon color="primary">mdi-open-in-new</v-icon>
            </button>
          </div>
          <div v-else class="empty-result">
            <v-icon size="40" color="grey lighten-1">mdi-file-search-outline</v-icon>
            <div class="text-subtitle-1 mt-3">没有找到可访问的有效来源</div>
            <div class="text-body-2 grey--text mt-1">尝试使用对象名、字段名或更具体的治理术语。</div>
          </div>
        </div>
      </section>

      <aside class="context-panel">
        <div class="context-section">
          <h2 class="text-subtitle-1 font-weight-bold mb-3">可信检索边界</h2>
          <div class="boundary-row">
            <v-icon small color="success">mdi-shield-check-outline</v-icon>
            <span>服务端业务域授权</span>
          </div>
          <div class="boundary-row">
            <v-icon small color="success">mdi-history</v-icon>
            <span>仅使用 active 版本</span>
          </div>
          <div class="boundary-row">
            <v-icon small color="success">mdi-link-variant</v-icon>
            <span>知识点级引用</span>
          </div>
        </div>

        <div v-if="isAdmin" class="context-section admin-section">
          <div class="d-flex align-center justify-space-between mb-3">
            <h2 class="text-subtitle-1 font-weight-bold mb-0">索引运行状态</h2>
            <v-btn icon small :loading="adminLoading" @click="loadAdmin">
              <v-icon small>mdi-refresh</v-icon>
            </v-btn>
          </div>
          <div class="metric-row">
            <span>有效文档</span><strong>{{ adminStatus.active_documents || 0 }}</strong>
          </div>
          <div class="metric-row">
            <span>待处理变更</span><strong>{{ statusCount('pending') }}</strong>
          </div>
          <div class="metric-row">
            <span>失败变更</span><strong :class="{ 'error--text': statusCount('failed') }">{{ statusCount('failed') }}</strong>
          </div>
          <v-divider class="my-4" />
          <div v-if="changeSets.length" class="change-list">
            <div v-for="item in changeSets.slice(0, 5)" :key="item.id" class="change-row">
              <div>
                <div class="text-body-2 font-weight-medium">{{ item.source_type }} · v{{ item.source_revision }}</div>
                <div class="text-caption grey--text">+{{ item.added_count }} / ~{{ item.modified_count }} / -{{ item.deleted_count }}</div>
              </div>
              <v-chip x-small outlined :color="changeColor(item.status)">{{ item.status }}</v-chip>
            </div>
          </div>
          <div v-else class="text-caption grey--text">暂无变更记录</div>
        </div>
      </aside>
    </main>

    <v-dialog v-model="sourceDialog" max-width="760">
      <v-card>
        <v-card-title class="d-flex justify-space-between">
          <span>治理来源核验</span>
          <v-btn icon @click="sourceDialog = false"><v-icon>mdi-close</v-icon></v-btn>
        </v-card-title>
        <v-divider />
        <v-card-text class="pt-5">
          <v-skeleton-loader v-if="sourceLoading" type="heading, paragraph" />
          <template v-else-if="sourceDetail">
            <div class="source-facts mb-4">
              <div><span>对象</span><strong>{{ sourceDetail.object_type }}</strong></div>
              <div><span>名称</span><strong>{{ sourceDetail.object_name }}</strong></div>
              <div><span>版本</span><strong>v{{ sourceDetail.object_version }}</strong></div>
              <div><span>Generation</span><strong>{{ sourceDetail.active_generation }}</strong></div>
            </div>
            <pre class="source-content">{{ sourceDetail.content }}</pre>
          </template>
        </v-card-text>
      </v-card>
    </v-dialog>
  </div>
</template>

<script>
import {
  askKnowledge,
  getKnowledgeCapabilities,
  getKnowledgeChangeSets,
  getKnowledgeSource,
  getKnowledgeSync,
  searchKnowledge
} from '@/api/governanceKnowledge'

export default {
  name: 'governance-knowledge-base',
  data () {
    return {
      action: 'ask',
      mode: 'auto',
      query: '',
      queryError: '',
      loading: false,
      error: '',
      answer: null,
      answerStatus: '',
      evidence: [],
      freshness: 'fresh',
      degradedComponents: [],
      capabilities: {},
      adminStatus: {},
      changeSets: [],
      adminLoading: false,
      sourceDialog: false,
      sourceLoading: false,
      sourceDetail: null,
      modes: [
        { label: '自动判断', value: 'auto' },
        { label: '精确', value: 'exact' },
        { label: '语义', value: 'semantic' },
        { label: '关系', value: 'relationship' },
        { label: '全局', value: 'global' }
      ]
    }
  },
  computed: {
    isAdmin () {
      const permissions = this.$store.getters.userInfo?.permissions || []
      return permissions.includes('knowledge:manage')
    },
    hasResult () {
      return Boolean(this.answerStatus || this.evidence.length)
    },
    freshnessLabel () {
      return { fresh: '知识已同步', updating: '更新处理中', degraded: '状态降级' }[this.freshness] || this.freshness
    },
    freshnessColor () {
      return { fresh: 'success', updating: 'warning', degraded: 'error' }[this.freshness] || 'grey'
    }
  },
  created () {
    getKnowledgeCapabilities().then(res => { this.capabilities = res.data || {} }).catch(() => {})
    if (this.isAdmin) this.loadAdmin()
  },
  methods: {
    async submit () {
      if (!this.query.trim()) {
        this.queryError = '请输入治理问题'
        return
      }
      this.queryError = ''
      this.error = ''
      this.loading = true
      this.answer = null
      this.answerStatus = ''
      this.evidence = []
      this.degradedComponents = []
      try {
        const request = { query: this.query.trim(), mode: this.mode }
        const response = this.action === 'ask' ? await askKnowledge(request) : await searchKnowledge(request)
        const data = response.data || {}
        this.degradedComponents = data.degraded_components || []
        if (this.action === 'ask') {
          this.answer = data.answer
          this.answerStatus = data.answer_status
          this.evidence = data.citations || []
          this.freshness = data.freshness_status || 'degraded'
        } else {
          this.answerStatus = 'retrieved'
          this.evidence = data.evidence || []
          this.freshness = this.evidence.some(item => item.freshness_status === 'updating') ? 'updating' : 'fresh'
        }
      } catch (error) {
        this.error = typeof error === 'string' ? error : '知识检索暂时不可用，请稍后重试。'
      } finally {
        this.loading = false
      }
    },
    async loadAdmin () {
      this.adminLoading = true
      try {
        const [status, changes] = await Promise.all([getKnowledgeSync(), getKnowledgeChangeSets(20)])
        this.adminStatus = status.data || {}
        this.changeSets = changes.data || []
      } catch (error) {
        this.error = '管理员状态加载失败。'
      } finally {
        this.adminLoading = false
      }
    },
    async openSource (item) {
      this.sourceDialog = true
      this.sourceLoading = true
      this.sourceDetail = null
      try {
        const response = await getKnowledgeSource(item.object_uid)
        this.sourceDetail = response.data
      } catch (error) {
        this.sourceDialog = false
        this.error = '来源已更新、删除或当前无权访问。'
      } finally {
        this.sourceLoading = false
      }
    },
    firstPoint (item) {
      return item.point_key || (item.point_keys && item.point_keys[0]) || '未标注知识点'
    },
    firstRevision (item) {
      return item.point_revision || (item.point_revisions && item.point_revisions[0]) || '-'
    },
    statusCount (status) {
      return (this.adminStatus.change_sets && this.adminStatus.change_sets[status]) || 0
    },
    changeColor (status) {
      return { canonical_active: 'success', complete: 'success', failed: 'error', degraded: 'warning', pending: 'primary' }[status] || 'grey'
    }
  }
}
</script>

<style lang="scss" scoped>
.knowledge-workbench {
  min-height: 100%;
  background: #f5f7fa;
  color: #1f2937;
}

.workbench-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 32px;
  padding: 36px 40px 28px;
  background: #fff;
  border-bottom: 1px solid #e5e9f0;
}

.status-cluster { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }

.workbench-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 24px;
  max-width: 1440px;
  margin: 0 auto;
  padding: 28px 40px 48px;
}

.query-panel, .context-section {
  background: #fff;
  border: 1px solid #e3e8ef;
  border-radius: 12px;
}

.query-panel { padding: 28px; min-height: 620px; }
.context-panel { display: flex; flex-direction: column; gap: 16px; }
.context-section { padding: 20px; }
.mode-switch { display: flex; justify-content: space-between; gap: 16px; }
.mode-select { max-width: 180px; }
.answer-block { padding: 20px; background: #f3f7fd; border-left: 3px solid #1976d2; border-radius: 8px; }
.answer-copy { white-space: pre-wrap; line-height: 1.8; margin: 0; }
.evidence-header { display: flex; justify-content: space-between; align-items: center; margin: 26px 0 10px; }
.evidence-list { border-top: 1px solid #e5e9f0; }
.evidence-row {
  width: 100%; display: grid; grid-template-columns: 38px minmax(0, 1fr) 24px; gap: 14px;
  align-items: start; padding: 18px 4px; border: 0; border-bottom: 1px solid #e5e9f0;
  background: transparent; color: inherit; text-align: left; cursor: pointer;
}
.evidence-row:hover { background: #f7f9fc; }
.evidence-row:focus-visible { outline: 2px solid #1976d2; outline-offset: 2px; }
.evidence-index { color: #78909c; font-size: 12px; font-weight: 700; padding-top: 3px; }
.evidence-main { min-width: 0; display: flex; flex-direction: column; }
.evidence-content { line-height: 1.65; overflow-wrap: anywhere; }
.evidence-meta { color: #637083; font-size: 12px; overflow-wrap: anywhere; }
.empty-result { text-align: center; padding: 56px 20px; border: 1px dashed #cfd8e3; border-radius: 10px; }
.boundary-row, .metric-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 7px 0; }
.boundary-row { justify-content: flex-start; color: #475569; }
.change-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid #edf0f4; }
.source-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.source-facts > div { display: flex; flex-direction: column; gap: 3px; }
.source-facts span { color: #637083; font-size: 12px; }
.source-content { white-space: pre-wrap; overflow-wrap: anywhere; padding: 16px; background: #f5f7fa; border-radius: 8px; font: 13px/1.65 monospace; }

@media (max-width: 960px) {
  .workbench-header { padding: 28px 24px; }
  .workbench-grid { grid-template-columns: 1fr; padding: 24px; }
  .context-panel { grid-row: 1; }
}

@media (max-width: 600px) {
  .workbench-header { flex-direction: column; gap: 16px; }
  .status-cluster { justify-content: flex-start; }
  .workbench-grid { padding: 16px; }
  .query-panel { padding: 20px 16px; }
  .mode-switch { flex-direction: column; }
  .mode-select { max-width: none; }
  .source-facts { grid-template-columns: 1fr; }
}
</style>
