// knowledge-graph.ts
import { f35Data } from './data'
import * as d3 from "d3";

interface GraphNode {
  id: string
  name: string
  type: 'aircraft' | 'country' | 'weapon' | 'military' | 'technology'
  description?: string
  image?: string
  color?: string
}

interface GraphLink {
  source: string
  target: string
  label: string
  strength?: number
}

/**
 * 知识图谱类，负责渲染和管理知识图谱
 */
export class KnowledgeGraph {
  private svg: d3.Selection<SVGSVGElement, unknown, null, undefined>
  private simulation: d3.Simulation<GraphNode, GraphLink>
  private zoom: d3.ZoomBehavior<SVGSVGElement, unknown>

  /**
   * 构造函数
   * @param container SVG容器元素
   */
  constructor(container: SVGSVGElement) {
    this.initSVG(container)
    this.initSimulation()
    this.initZoom()
    this.renderGraph()
  }

  private initSVG(container: SVGSVGElement) {
    const width = 1200, height = 800

    this.svg = d3.select(container)
      .attr('width', width)
      .attr('height', height)
      .attr('viewBox', [0, 0, width, height])
  }

  private initSimulation() {
    this.simulation = d3.forceSimulation<GraphNode>(f35Data.nodes)
      .force('link', d3.forceLink<GraphNode, GraphLink>(f35Data.links).id(d => d.id).distance(100))
      .force('charge', d3.forceManyBody().strength(-300))
      .force('center', d3.forceCenter(600, 400))
      .force('collision', d3.forceCollide().radius(d => (d as any).size + 10))
  }

  private initZoom() {
    this.zoom = d3.zoom<SVGSVGElement, unknown>()
      .scaleExtent([0.1, 4])
      .on('zoom', (event) => {
        this.svg.selectAll('g.container').attr('transform', event.transform)
      })

    this.svg.call(this.zoom)
  }

  private renderGraph() {
    const container = this.svg.append('g').attr('class', 'container')

    // 绘制连线
    const links = container.append('g')
      .selectAll('line')
      .data(f35Data.links)
      .join('line')
      .attr('class', 'link')
      .style('stroke', '#4CAF50')
      .style('stroke-width', 2)
      .style('stroke-opacity', 0.6)

    // 绘制节点
    const nodes = container.append('g')
      .selectAll('g.node')
      .data(f35Data.nodes)
      .join('g')
      .attr('class', 'node')
      .attr('data-node-id', d => d.id) // 直接添加data-node-id属性
      .call(d3.drag<SVGGElement, GraphNode>()
        .on('start', this.dragStarted.bind(this))
        .on('drag', this.dragged.bind(this))
        .on('end', this.dragEnded.bind(this))
      )

    // 节点圆形
    nodes.append('circle')
      .attr('r', d => d.size || 20)
      .style('fill', d => d.color || '#ccc')
      .style('stroke', '#fff')
      .style('stroke-width', 2)

    // 节点文字
    nodes.append('text')
      .text(d => d.name)
      .attr('text-anchor', 'middle')
      .attr('dy', -15)
      .style('fill', '#fff')
      .style('font-size', '12px')
      .style('pointer-events', 'none')

    // 力导向模拟更新
    this.simulation.on('tick', () => {
      links
        .attr('x1', d => (d.source as any).x)
        .attr('y1', d => (d.source as any).y)
        .attr('x2', d => (d.target as any).x)
        .attr('y2', d => (d.target as any).y)

      nodes.attr('transform', d => `translate(${d.x},${d.y})`)
    })
  }

  private dragStarted(event: any, d: GraphNode) {
    if (!event.active) this.simulation.alphaTarget(0.3).restart()
    d.fx = d.x
    d.fy = d.y
  }

  private dragged(event: any, d: GraphNode) {
    d.fx = event.x
    d.fy = event.y
  }

  private dragEnded(event: any, d: GraphNode) {
    if (!event.active) this.simulation.alphaTarget(0)
    d.fx = null
    d.fy = null
  }

  /**
   * 获取选中的节点信息
   * @param nodeId 节点ID
   * @returns 节点信息
   */
  public getNodeInfo(nodeId: string): GraphNode | undefined {
    return f35Data.nodes.find(node => node.id === nodeId)
  }

  /**
   * 根据索引获取节点信息
   * @param index 节点索引
   * @returns 节点信息
   */
  public getNodeInfoByIndex(index: number): GraphNode | undefined {
    return f35Data.nodes[index]
  }
}
