<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
import { graphApi } from '@/api/graph'
import { Network, RefreshCw, Info, Maximize2, Hexagon } from 'lucide-vue-next'
import type { KGNode, KGEdge } from '@/api/graph'

const nodes = ref<KGNode[]>([])
const edges = ref<KGEdge[]>([])
const stats = ref({ node_count: 0, edge_count: 0 })
const isLoading = ref(false)
const selectedEntity = ref<KGNode | null>(null)
const entityContext = ref('')
const isBuilding = ref(false)
const chartRef = ref<HTMLDivElement>()

const typeColors: Record<string, string> = {
  person: '#f87171', concept: '#60a5fa', topic: '#a78bfa',
  task: '#fbbf24', event: '#fb923c', document: '#9ca3af', default: '#4b5563',
}

function getColor(type: string) { return typeColors[type] || typeColors.default }

async function loadGraph() {
  isLoading.value = true
  try {
    const response = await graphApi.get()
    nodes.value = response.data.nodes
    edges.value = response.data.edges
    stats.value = response.data.stats
    await nextTick()
    renderChart()
  } catch (e) { console.error('加载图谱失败:', e) }
  isLoading.value = false
}

async function buildGraph() {
  isBuilding.value = true
  try {
    await graphApi.build()
    setTimeout(() => loadGraph(), 2000)
  } catch (e) { console.error('构建失败:', e) }
  isBuilding.value = false
}

async function selectEntity(node: KGNode) {
  selectedEntity.value = node
  entityContext.value = 'LOADING...'
  try {
    const response = await graphApi.getEntityContext(node.name)
    entityContext.value = response.data.context
  } catch (e) { entityContext.value = 'Failed to load context' }
}

function renderChart() {
  if (!chartRef.value) return
  // @ts-ignore
  if (!window.echarts) return
  // @ts-ignore
  const echarts = window.echarts
  const chart = echarts.init(chartRef.value, 'dark')

  const option = {
    backgroundColor: 'transparent',
    tooltip: {
      trigger: 'item',
      backgroundColor: 'rgba(10, 15, 26, 0.95)',
      borderColor: 'rgba(0, 245, 212, 0.2)',
      textStyle: { color: '#e8f4f8', fontFamily: 'JetBrains Mono, monospace', fontSize: 12 },
      formatter: (params: any) => {
        if (params.dataType === 'node') {
          return `<b style="color:#00f5d4">${params.data.name}</b><br/><span style="color:#7eb8c9">Type: ${params.data.type || 'unknown'}</span>`
        }
        return `<span style="color:#7eb8c9">${params.data.sourceName}</span> → <span style="color:#a78bfa">${params.data.relation}</span> → <span style="color:#7eb8c9">${params.data.targetName}</span>`
      },
    },
    series: [{
      type: 'graph',
      layout: 'force',
      symbolSize: (_val: unknown, params: any) => 18 + (params.data.importance || 0.5) * 40,
      roam: true,
      label: {
        show: true,
        fontSize: 10,
        color: '#7eb8c9',
        fontFamily: 'JetBrains Mono, monospace',
        formatter: (params: any) => params.data.name?.substring(0, 14) || '',
      },
      lineStyle: { color: 'rgba(0, 245, 212, 0.15)', width: 1.5 },
      emphasis: {
        focus: 'adjacency',
        lineStyle: { width: 3, color: 'rgba(0, 245, 212, 0.5)' },
      },
      edgeSymbol: ['circle', 'arrow'],
      edgeSymbolSize: [4, 8],
      data: nodes.value.map(n => ({
        id: n.id, name: n.name, type: n.type,
        importance: n.importance || 0.5,
        itemStyle: {
          color: getColor(n.type),
          borderColor: getColor(n.type),
          borderWidth: 2,
          shadowColor: getColor(n.type),
          shadowBlur: 8,
        },
      })),
      links: edges.value.map(e => {
        const src = nodes.value.find(n => n.id === e.source)
        const tgt = nodes.value.find(n => n.id === e.target)
        return {
          source: e.source, target: e.target,
          sourceName: src?.name || '', targetName: tgt?.name || '',
          relation: e.relation,
          lineStyle: { color: 'rgba(0, 245, 212, 0.15)' },
        }
      }),
      force: { repulsion: 150, gravity: 0.05, edgeLength: [60, 200], layoutAnimation: true },
    }],
  }
  chart.setOption(option)
  chart.on('click', (params: any) => {
    if (params.dataType === 'node') {
      const node = nodes.value.find(n => n.id === params.data.id)
      if (node) selectEntity(node)
    }
  })
}

onMounted(() => {
  loadGraph()
  const script = document.createElement('script')
  script.src = 'https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js'
  script.onload = () => renderChart()
  document.head.appendChild(script)
})
</script>

<template>
  <div class="graph-view">
    <!-- Header -->
    <div class="page-header">
      <div class="header-left">
        <div class="header-icon"><Hexagon :size="20" /></div>
        <div class="header-text">
          <h1>KNOWLEDGE GRAPH</h1>
          <span class="header-sub">{{ stats.node_count }} nodes · {{ stats.edge_count }} relations</span>
        </div>
      </div>
      <button class="build-btn" @click="buildGraph" :disabled="isBuilding">
        <RefreshCw :size="14" :class="{ spin: isBuilding }" />
        {{ isBuilding ? 'BUILDING...' : 'REBUILD' }}
      </button>
    </div>

    <!-- Type legend -->
    <div class="type-legend">
      <div v-for="(color, type) in typeColors" :key="type" class="legend-item">
        <div class="legend-dot" :style="{ background: color, boxShadow: `0 0 6px ${color}` }"></div>
        <span>{{ type.toUpperCase() }}</span>
      </div>
    </div>

    <!-- Main area: chart + panel -->
    <div class="main-area">
      <div class="graph-container">
        <div v-if="nodes.length === 0 && !isLoading" class="empty-state">
          <div class="empty-icon">
            <div class="e-ring r1"></div>
            <div class="e-ring r2"></div>
            <Network :size="32" />
          </div>
          <div class="empty-title">NO GRAPH DATA</div>
          <div class="empty-sub">Upload documents and rebuild the graph</div>
        </div>
        <div ref="chartRef" v-show="nodes.length > 0" class="chart-canvas"></div>
      </div>

      <!-- Entity panel -->
      <div class="entity-panel" v-if="selectedEntity">
        <div class="panel-title">
          <Info :size="14" />
          <span>ENTITY DETAIL</span>
          <button class="close-panel" @click="selectedEntity = null">×</button>
        </div>
        <div class="entity-name" :style="{ color: getColor(selectedEntity.type) }">
          {{ selectedEntity.name }}
        </div>
        <div class="entity-type-tag" :style="{ color: getColor(selectedEntity.type), borderColor: getColor(selectedEntity.type) + '40' }">
          {{ (selectedEntity.type || 'unknown').toUpperCase() }}
        </div>
        <div class="entity-context">{{ entityContext }}</div>
      </div>
    </div>

    <!-- Relation list -->
    <div class="relations-section" v-if="edges.length > 0">
      <div class="section-label">// RELATIONS ({{ edges.length }})</div>
      <div class="relations-scroll">
        <div v-for="edge in edges" :key="edge.id" class="rel-item">
          <span class="rel-node">{{ nodes.find(n => n.id === edge.source)?.name?.slice(0, 16) || edge.source.slice(0, 8) }}</span>
          <div class="rel-arrow">
            <div class="arrow-line"></div>
            <span class="rel-type-badge">{{ edge.relation }}</span>
          </div>
          <span class="rel-node">{{ nodes.find(n => n.id === edge.target)?.name?.slice(0, 16) || edge.target.slice(0, 8) }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.graph-view {
  height: 100%;
  overflow-y: auto;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header-left { display: flex; align-items: center; gap: 14px; }

.header-icon { color: var(--accent-cyan); filter: drop-shadow(0 0 8px var(--accent-cyan)); }

h1 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.15em;
  color: var(--text-primary);
  margin: 0;
}

.header-sub { font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); letter-spacing: 0.1em; }

.build-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  background: var(--accent-cyan-dim);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius-md);
  color: var(--accent-cyan);
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.12em;
  transition: all var(--transition-fast);
}

.build-btn:hover:not(:disabled) {
  background: rgba(0, 245, 212, 0.2);
  box-shadow: var(--glow-cyan);
}

.spin { animation: spin 1s linear infinite; }

.type-legend {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--text-dim);
}

.legend-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}

.main-area {
  display: flex;
  gap: 16px;
  flex: 1;
  min-height: 450px;
}

.graph-container {
  flex: 1;
  background: var(--bg-card);
  border: 1px solid var(--border-dim);
  border-radius: var(--radius-lg);
  overflow: hidden;
  position: relative;
  min-height: 400px;
}

.empty-state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.empty-icon {
  position: relative;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
}

.e-ring {
  position: absolute;
  border-radius: 50%;
  border: 1px solid var(--accent-cyan);
  opacity: 0.2;
}
.r1 { width: 60px; height: 60px; animation: spin 6s linear infinite; border-style: dashed; }
.r2 { width: 40px; height: 40px; animation: spin 4s linear infinite reverse; }

.empty-title {
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}

.empty-sub {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-muted);
}

.chart-canvas {
  width: 100%;
  height: 100%;
  min-height: 400px;
}

/* Entity panel */
.entity-panel {
  width: 260px;
  background: var(--bg-card);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius-lg);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: fade-in-up 0.2s ease;
  overflow-y: auto;
}

.panel-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.15em;
  color: var(--accent-cyan);
  margin-bottom: 4px;
}

.close-panel {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 0 2px;
}

.close-panel:hover { color: var(--accent-red); }

.entity-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.entity-type-tag {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.12em;
  padding: 2px 8px;
  border: 1px solid;
  border-radius: 4px;
  width: fit-content;
}

.entity-context {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.7;
  white-space: pre-wrap;
}

/* Relations */
.relations-section { }

.section-label {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.15em;
  color: var(--text-dim);
  margin-bottom: 10px;
}

.relations-scroll {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 180px;
  overflow-y: auto;
}

.rel-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: var(--bg-card);
  border: 1px solid var(--border-dim);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: 10px;
}

.rel-node { color: var(--accent-cyan); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }

.rel-arrow {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.arrow-line {
  width: 20px;
  height: 1px;
  background: linear-gradient(90deg, var(--border-mid), var(--accent-cyan), var(--border-mid));
}

.rel-type-badge {
  font-size: 8px;
  letter-spacing: 0.08em;
  color: var(--accent-purple);
  padding: 1px 5px;
  background: var(--accent-purple-dim);
  border-radius: 3px;
  white-space: nowrap;
}
</style>
