<script setup lang="ts">
import type {ChatTimelineKnowledgeHit} from './types';

import {BookOpenText, X} from '@easyflow/icons';
import {computed, ref} from 'vue';

interface KnowledgeCitation {
  id: string;
  isFaq: boolean;
  label: string;
  items: ChatTimelineKnowledgeHit[];
}

const props = defineProps<{
  items: ChatTimelineKnowledgeHit[];
}>();

const activeId = ref('');

function asText(value: unknown) {
  return value === null || value === undefined ? '' : String(value).trim();
}

function isFaqHit(item: ChatTimelineKnowledgeHit) {
  if (item.faqCollection === true) {
    return true;
  }
  return asText(item.knowledgeType).toUpperCase() === 'FAQ';
}

function sourceFileName(item: ChatTimelineKnowledgeHit) {
  return asText(item.sourceFileName ?? item.metadata?.sourceFileName);
}

function resolveLabel(item: ChatTimelineKnowledgeHit, index: number) {
  if (isFaqHit(item)) {
    return asText(item.knowledgeName) || `知识库 ${index + 1}`;
  }
  return (
    asText(item.documentName) ||
    sourceFileName(item) ||
    asText(item.knowledgeName) ||
    `知识库 ${index + 1}`
  );
}

function resolveGroupKey(item: ChatTimelineKnowledgeHit, index: number) {
  const label = resolveLabel(item, index);
  if (isFaqHit(item)) {
    return `faq:${label || asText(item.knowledgeId) || index}`;
  }
  const sourceName = asText(item.documentName) || sourceFileName(item);
  return [
    'doc',
    sourceName || asText(item.sourceUri) || label || asText(item.documentId),
    asText(item.knowledgeId),
  ]
    .filter(Boolean)
    .join(':') || `doc:${index}`;
}

function formatScore(score: ChatTimelineKnowledgeHit['score']) {
  if (score === null || score === undefined || score === '') {
    return '';
  }
  const numberValue = Number(score);
  if (Number.isFinite(numberValue)) {
    return numberValue > 1 ? numberValue.toFixed(2) : `${Math.round(numberValue * 100)}%`;
  }
  return String(score);
}

function resolveChunkContent(item: ChatTimelineKnowledgeHit) {
  return asText(item.chunkContent ?? item.content ?? item.text ?? item.summary);
}

function citationTypeLabel(citation: KnowledgeCitation) {
  return citation.isFaq ? 'FAQ' : '文档';
}

const citations = computed<KnowledgeCitation[]>(() => {
  const groups = new Map<string, KnowledgeCitation>();
  props.items.forEach((item, index) => {
    const key = resolveGroupKey(item, index);
    const found = groups.get(key);
    if (found) {
      found.items.push(item);
      return;
    }
    groups.set(key, {
      id: key,
      isFaq: isFaqHit(item),
      label: resolveLabel(item, index),
      items: [item],
    });
  });
  return [...groups.values()];
});

const activeCitation = computed(
  () => citations.value.find((citation) => citation.id === activeId.value),
);

function toggleCitation(id: string) {
  activeId.value = activeId.value === id ? '' : id;
}
</script>

<template>
  <section v-if="citations.length > 0" class="chat-knowledge-card">
    <div class="chat-knowledge-card__pills" aria-label="知识库引注">
      <button
        v-for="citation in citations"
        :key="citation.id"
        type="button"
        class="chat-knowledge-card__pill"
        :class="{ 'is-active': citation.id === activeId }"
        :aria-expanded="citation.id === activeId"
        @click="toggleCitation(citation.id)"
      >
        <span class="chat-knowledge-card__icon" aria-hidden="true">
          <BookOpenText :size="13" :stroke-width="2" />
        </span>
        <span class="chat-knowledge-card__label">{{ citation.label }}</span>
        <span
          v-if="citation.items.length > 1"
          class="chat-knowledge-card__count"
        >
          · {{ citation.items.length }}
        </span>
      </button>
    </div>

    <div
      v-if="activeCitation"
      class="chat-knowledge-card__popover"
      role="dialog"
      :aria-label="activeCitation.label"
    >
      <div class="chat-knowledge-card__popover-head">
        <div class="chat-knowledge-card__popover-main">
          <div class="chat-knowledge-card__popover-title">
            {{ activeCitation.label }}
          </div>
          <div class="chat-knowledge-card__popover-meta">
            <span>{{ citationTypeLabel(activeCitation) }}</span>
            <span>{{ activeCitation.items.length }} 个片段</span>
          </div>
        </div>
        <button
          type="button"
          class="chat-knowledge-card__close"
          aria-label="关闭"
          @click="activeId = ''"
        >
          <X :size="14" :stroke-width="2" />
        </button>
      </div>
      <div class="chat-knowledge-card__hits">
        <article
          v-for="(hit, index) in activeCitation.items"
          :key="hit.chunkId || hit.id || index"
          class="chat-knowledge-card__hit"
        >
          <div class="chat-knowledge-card__meta">
            <span v-if="hit.knowledgeName">{{ hit.knowledgeName }}</span>
            <span v-if="hit.documentName && !isFaqHit(hit)">
              {{ hit.documentName }}
            </span>
            <span v-if="formatScore(hit.score)">
              {{ formatScore(hit.score) }}
            </span>
          </div>
          <blockquote class="chat-knowledge-card__content">
            {{ resolveChunkContent(hit) }}
          </blockquote>
        </article>
      </div>
    </div>
  </section>
</template>

<style scoped>
.chat-knowledge-card {
  position: relative;
  max-width: min(78%, 680px);
  margin-top: 10px;
}

.chat-knowledge-card__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chat-knowledge-card__pill {
  display: inline-flex;
  gap: 5px;
  align-items: center;
  max-width: 260px;
  min-height: 26px;
  padding: 3px 9px;
  color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  background: color-mix(in srgb, var(--el-color-primary-light-9) 78%, white);
  border: 1px solid var(--el-color-primary-light-7);
  border: 1px solid
    color-mix(in srgb, var(--el-color-primary-light-7) 72%, white);
  border-radius: 999px;
  transition:
    color 0.18s ease,
    background-color 0.18s ease,
    border-color 0.18s ease,
    transform 0.18s ease;
  cursor: pointer;
}

.chat-knowledge-card__pill:hover,
.chat-knowledge-card__pill.is-active {
  color: var(--el-color-primary);
  background: var(--el-color-primary-light-8);
  background: color-mix(in srgb, var(--el-color-primary-light-8) 80%, white);
  border-color: var(--el-color-primary-light-5);
}

.chat-knowledge-card__pill:hover {
  transform: translateY(-1px);
}

.chat-knowledge-card__pill:focus-visible,
.chat-knowledge-card__close:focus-visible {
  outline: 2px solid var(--el-color-primary-light-5);
  outline-offset: 2px;
}

.chat-knowledge-card__icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--el-color-primary);
  color: color-mix(
    in srgb,
    var(--el-color-primary) 78%,
    var(--el-text-color-regular)
  );
}

.chat-knowledge-card__label {
  overflow: hidden;
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-knowledge-card__count {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  color: var(--el-text-color-secondary);
  color: color-mix(
    in srgb,
    var(--el-color-primary) 62%,
    var(--el-text-color-secondary)
  );
  text-align: center;
}

.chat-knowledge-card__popover {
  width: min(540px, calc(100vw - 48px));
  padding: 14px;
  margin-top: 8px;
  color: var(--el-text-color-primary);
  background: var(--el-bg-color-overlay);
  background: color-mix(in srgb, var(--el-bg-color-overlay) 94%, white);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 12px;
  box-shadow: var(--el-box-shadow-light);
}

.chat-knowledge-card__popover-head {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 12px;
}

.chat-knowledge-card__popover-main {
  min-width: 0;
}

.chat-knowledge-card__popover-title {
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  line-height: 20px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-knowledge-card__popover-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
  font-size: 12px;
  line-height: 18px;
  color: var(--el-text-color-secondary);
}

.chat-knowledge-card__popover-meta span + span::before {
  margin-right: 6px;
  color: var(--el-border-color);
  content: "·";
}

.chat-knowledge-card__close {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  color: var(--el-text-color-secondary);
  background: transparent;
  border: 0;
  border-radius: 6px;
}

.chat-knowledge-card__close:hover {
  color: var(--el-text-color-primary);
  background: var(--el-fill-color-light);
}

.chat-knowledge-card__hits {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 360px;
  overflow: auto;
}

.chat-knowledge-card__hit {
  padding: 10px 10px 10px 12px;
  background: var(--el-fill-color-lighter);
  background: color-mix(in srgb, var(--el-fill-color-lighter) 72%, white);
  border-left: 2px solid var(--el-color-primary-light-5);
  border-radius: 8px;
}

.chat-knowledge-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
  font-size: 12px;
  line-height: 18px;
  color: var(--el-text-color-secondary);
}

.chat-knowledge-card__meta span + span::before {
  margin-right: 6px;
  color: var(--el-border-color);
  content: "/";
}

.chat-knowledge-card__content {
  margin: 0;
  font-size: 13px;
  line-height: 20px;
  color: var(--el-text-color-regular);
  white-space: pre-wrap;
  max-height: 200px;
  overflow: auto;
}

@supports not (color: color-mix(in srgb, red, blue)) {
  .chat-knowledge-card__pill {
    border-color: var(--el-color-primary-light-7);
  }
}
</style>
