<script setup lang="ts">
import { computed, onMounted, ref, nextTick } from 'vue'
import type { PlayerTreeNode } from '@engine/types'
import dagre from 'dagre'

const props = defineProps<{
  node: PlayerTreeNode | null
  scenes?: Record<string, { thumbnail?: string }>
}>()

const emit = defineEmits<{
  selectScene: [sceneId: string]
  selectGateway: [chapterId: string]
}>()

interface FlowNode {
  id: string
  sceneId: string
  label: string
  thumbnail?: string
  visited: boolean
  isMystery: boolean
  isGateway: boolean
  gatewayChapterId?: string
  locked: boolean
  lockHint?: string
  x: number
  y: number
  w: number
  h: number
}

interface FlowEdge {
  from: string
  to: string
  visited: boolean
  points: { x: number; y: number }[]
}

const nodes = ref<FlowNode[]>([])
const edges = ref<FlowEdge[]>([])
const containerW = ref(800)
const containerH = ref(400)

function buildFlow(root: PlayerTreeNode) {
  const dagreNodes: { id: string; sceneId: string; thumbnail?: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; isGateway: boolean; gatewayChapterId?: string; locked: boolean; lockHint?: string }[] = []
  const dagreEdges: { from: string; to: string; visited: boolean }[] = []

  function walk(node: PlayerTreeNode, parentId: string | null) {
    if (node.isGateway) {
      const dagreId = parentId ? `${parentId}/gw_${node.gatewayChapterId}` : `gw_${node.gatewayChapterId}`
      dagreNodes.push({
        id: dagreId,
        sceneId: '',
        parent: parentId,
        label: node.label,
        visited: false,
        isMystery: false,
        isGateway: true,
        gatewayChapterId: node.gatewayChapterId,
        locked: node.locked,
      })
      if (parentId) {
        dagreEdges.push({ from: parentId, to: dagreId, visited: false })
      }
      return
    }

    if (node.visited) {
      const dagreId = parentId ? `${parentId}/${node.sceneId}` : node.sceneId
      dagreNodes.push({
        id: dagreId,
        sceneId: node.sceneId,
        thumbnail: props.scenes?.[node.sceneId]?.thumbnail,
        parent: parentId,
        label: node.label,
        visited: true,
        isMystery: false,
        isGateway: false,
        locked: node.locked,
        lockHint: node.lockHint,
      })
      if (parentId) {
        dagreEdges.push({ from: parentId, to: dagreId, visited: true })
      }

      const unvisited: PlayerTreeNode[] = []
      for (const child of node.children) {
        if (child.visited || child.isGateway) {
          walk(child, dagreId)
        } else {
          unvisited.push(child)
        }
      }

      if (unvisited.length > 0) {
        const mysteryId = `${dagreId}/__mystery`
        dagreNodes.push({
          id: mysteryId,
          sceneId: '',
          parent: dagreId,
          label: '? ?',
          visited: false,
          isMystery: true,
          isGateway: false,
          locked: true,
        })
        dagreEdges.push({ from: dagreId, to: mysteryId, visited: false })
      }
    }
  }

  walk(root, null)

  if (dagreNodes.length === 0) return

  const g = new dagre.graphlib.Graph()
  g.setGraph({ rankdir: 'LR', nodesep: 60, ranksep: 200, marginx: 24, marginy: 24 })
  g.setDefaultEdgeLabel(() => ({}))

  const baseW = 128
  const baseH = 48
  const thumbH = 78

  for (const n of dagreNodes) {
    const hasThumb = !!n.thumbnail
    g.setNode(n.id, { width: hasThumb ? baseW + 20 : baseW, height: hasThumb ? baseH + thumbH + 8 : baseH })
  }
  for (const e of dagreEdges) {
    g.setEdge(e.from, e.to)
  }

  dagre.layout(g)

  const resultNodes: FlowNode[] = dagreNodes.map((n) => {
    const pos = g.node(n.id)
    const hasThumb = !!n.thumbnail
    const nw = hasThumb ? baseW + 20 : baseW
    const nh = hasThumb ? baseH + thumbH + 8 : baseH
    return {
      id: n.id,
      sceneId: n.sceneId,
      thumbnail: n.thumbnail,
      label: n.label,
      visited: n.visited,
      isMystery: n.isMystery,
      isGateway: n.isGateway || false,
      gatewayChapterId: n.gatewayChapterId,
      locked: n.locked,
      lockHint: n.lockHint,
      x: pos.x - nw / 2,
      y: pos.y - nh / 2,
      w: nw,
      h: nh,
    }
  })

  const nodeMap = new Map(resultNodes.map((n) => [n.id, n]))

  const resultEdges: FlowEdge[] = dagreEdges.map((e) => {
    const edge = g.edge(e.from, e.to)
    const pts = edge.points || []
    if (pts.length >= 2) {
      const src = nodeMap.get(e.from)
      const dst = nodeMap.get(e.to)
      if (src) {
        pts[0] = { x: src.x + src.w, y: src.y + src.h / 2 }
      }
      if (dst) {
        pts[pts.length - 1] = { x: dst.x, y: dst.y + dst.h / 2 }
      }
    }
    return {
      from: e.from,
      to: e.to,
      visited: e.visited,
      points: pts,
    }
  })

  nodes.value = resultNodes
  edges.value = resultEdges

  const maxX = resultNodes.reduce((m, n) => Math.max(m, n.x + n.w), 0) + 40
  const maxY = resultNodes.reduce((m, n) => Math.max(m, n.y + n.h), 0) + 40
  containerW.value = Math.max(400, maxX)
  containerH.value = Math.max(100, maxY)
}

function buildFromRoot() {
  if (props.node) buildFlow(props.node)
}

onMounted(() => {
  nextTick(buildFromRoot)
})

function edgePath(e: FlowEdge): string {
  if (e.points.length < 2) return ''
  const r = 5
  let d = `M ${e.points[0].x} ${e.points[0].y}`

  for (let i = 1; i < e.points.length; i++) {
    const a = e.points[i - 1]
    const b = e.points[i]
    const dx = b.x - a.x
    const dy = b.y - a.y

    if (Math.abs(dy) < 0.5) {
      d += ` L ${b.x} ${b.y}`
    } else if (Math.abs(dx) < 0.5) {
      d += ` L ${b.x} ${b.y}`
    } else {
      const cr = Math.min(r, Math.abs(dx) / 2, Math.abs(dy) / 2)
      const midX = b.x
      const midY = a.y
      d += ` L ${midX - Math.sign(dx) * cr} ${midY}`
      if (cr > 0.5) d += ` Q ${midX} ${midY} ${midX} ${midY + Math.sign(dy) * cr}`
      d += ` L ${b.x} ${b.y}`
    }
  }
  return d
}

const svgW = computed(() => containerW.value)
const svgH = computed(() => containerH.value)
</script>

<template>
  <div class="tree-flow">
    <div v-if="!node || nodes.length === 0" class="flow-empty">暂无故事数据</div>
    <svg
      v-else
      class="flow-svg"
      :viewBox="`0 0 ${svgW} ${svgH}`"
      :width="svgW"
      :height="svgH"
    >
      <defs>
        <linearGradient id="edge-gold" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#8b6914"/>
          <stop offset="50%" stop-color="#e0c060"/>
          <stop offset="100%" stop-color="#8b6914"/>
        </linearGradient>
        <filter id="edge-glow">
          <feGaussianBlur stdDeviation="0.6" result="b"/>
          <feMerge>
            <feMergeNode in="b"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>
      </defs>

      <g
        v-for="edge in edges"
        :key="edge.from + '-' + edge.to"
      >
        <path
          v-if="edge.visited"
          :d="edgePath(edge)"
          fill="none"
          stroke="url(#edge-gold)"
          stroke-width="1.6"
          filter="url(#edge-glow)"
          opacity="0.18"
        />
        <path
          v-if="edge.visited"
          :d="edgePath(edge)"
          fill="none"
          stroke="#c9a84c"
          stroke-width="1.4"
          class="edge-main"
        />
        <path
          v-if="edge.visited"
          :d="edgePath(edge)"
          fill="none"
          stroke="#e0c060"
          stroke-width="0.7"
          stroke-dasharray="2 35"
          class="edge-flow"
          opacity="0.35"
        />
        <path
          v-if="!edge.visited"
          :d="edgePath(edge)"
          fill="none"
          stroke="#333"
          stroke-width="1"
          stroke-dasharray="5 4"
        />
      </g>
    </svg>

    <div class="flow-nodes" :style="{ width: svgW + 'px', height: svgH + 'px' }">
      <div
        v-for="n in nodes"
        :key="n.id"
        class="flow-node"
        :class="{ visited: n.visited, mystery: n.isMystery, locked: n.locked, gateway: n.isGateway, clickable: (n.visited && n.sceneId) || n.isGateway }"
        :style="{ left: n.x + 'px', top: n.y + 'px', width: n.w + 'px', height: n.h + 'px' }"
        :title="n.lockHint || ''"
        @click="n.isGateway ? emit('selectGateway', n.gatewayChapterId!) : n.visited && n.sceneId && emit('selectScene', n.sceneId)"
      >
        <img v-if="n.thumbnail" :src="n.thumbnail" class="node-thumb" />
        <span class="node-icon">{{ n.visited ? '✦' : n.isMystery ? '?' : n.isGateway ? '►' : '⬜' }}</span>
        <span class="node-label">{{ n.label }}</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
.tree-flow {
  position: relative;
  overflow: auto;
  max-height: 320px;
}

.flow-svg {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 0;
}

.flow-nodes {
  position: relative;
  z-index: 1;
}

.flow-node {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 4px 8px 6px;
  border-radius: 4px;
  font-size: 12px;
  transition: all 0.15s;
  overflow: hidden;
}

.flow-node:has(.node-thumb) {
  padding: 4px 4px 6px;
}

.node-thumb {
  width: 128px;
  height: 72px;
  object-fit: cover;
  border-radius: 3px;
  border: 1px solid rgba(255,255,255,0.06);
}

.flow-node.clickable {
  cursor: pointer;
}

.flow-node.clickable:hover {
  background: rgba(201, 168, 76, 0.22);
  border-color: rgba(201, 168, 76, 0.5);
}

.flow-node.gateway {
  background: rgba(201, 168, 76, 0.04);
  border: 1px dashed rgba(201, 168, 76, 0.2);
}

.flow-node.gateway:hover {
  background: rgba(201, 168, 76, 0.12);
  border-color: rgba(201, 168, 76, 0.4);
}

.flow-node.gateway .node-icon {
  color: #c9a84c;
}

.flow-node.gateway.locked {
  opacity: 0.4;
}

.flow-node.visited {
  background: rgba(201, 168, 76, 0.12);
  border: 1px solid rgba(201, 168, 76, 0.3);
}

.flow-node.locked {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.flow-node.mystery {
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.12);
  justify-content: center;
}

.node-icon {
  font-size: 12px;
  flex-shrink: 0;
}

.flow-node.visited .node-icon {
  color: #c9a84c;
}

.flow-node.locked .node-icon,
.flow-node.mystery .node-icon {
  color: #444;
}

.node-label {
  color: #ddd;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.flow-node.locked .node-label {
  color: #555;
}

.flow-node.mystery .node-label {
  color: #444;
}

.flow-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100px;
  font-size: 13px;
  color: #444;
}

@keyframes flowDash {
  to { stroke-dashoffset: -23; }
}

.edge-main {
  stroke-linecap: round;
}

.edge-flow {
  stroke-linecap: round;
  animation: flowDash 5s linear infinite;
}
</style>
