import { ref, computed, shallowRef, triggerRef } from 'vue'
import type { GameData, SceneNode, Choice } from '@engine/types'

export function useGraphEditor() {
  const gameData = shallowRef<GameData>({ scenes: {}, startScene: '', variables: {} })
  const selectedNodeId = ref<string | null>(null)
  const startSceneId = ref('')

  const sceneList = computed(() =>
    Object.values(gameData.value.scenes).map((s) => ({ id: s.id, label: s.id })),
  )

  const sceneNodes = computed(() =>
    Object.values(gameData.value.scenes).map((s) => ({ id: s.id, label: s.id })),
  )

  const sceneEdges = computed(() => {
    const result: { id: string; source: string; target: string; label?: string }[] = []
    for (const [id, scene] of Object.entries(gameData.value.scenes)) {
      if (scene.choices) {
        let ci = 0
        for (const c of scene.choices) {
          if (c.targetScene) {
            result.push({ id: `${id}_choice_${ci}`, source: id, target: c.targetScene, label: c.text.slice(0, 10) })
          }
          ci++
        }
      }
      if (scene.nextScene) {
        result.push({ id: `${id}_next`, source: id, target: scene.nextScene, label: '\u2192 \u9ed8\u8ba4' })
      }
      if (scene.qte) {
        if (scene.qte.successScene)
          result.push({ id: `${id}_qte_s`, source: id, target: scene.qte.successScene, label: 'QTE\u6210\u529f' })
        if (scene.qte.failScene)
          result.push({ id: `${id}_qte_f`, source: id, target: scene.qte.failScene, label: 'QTE\u5931\u8d25' })
      }
    }
    return result
  })

  const selectedScene = computed(() => {
    if (!selectedNodeId.value) return null
    return gameData.value.scenes[selectedNodeId.value] ?? null
  })

  function trigger() {
    triggerRef(gameData)
  }

  function loadJSON(json: GameData) {
    gameData.value = JSON.parse(JSON.stringify(json))
    trigger()
    startSceneId.value = json.startScene || ''
    selectedNodeId.value = null
  }

  function exportJSON(): GameData {
    return JSON.parse(JSON.stringify({ ...gameData.value, startScene: startSceneId.value }))
  }

  function generateId(): string {
    let i = Object.keys(gameData.value.scenes).length + 1
    while (gameData.value.scenes[`scene_${i}`]) i++
    return `scene_${i}`
  }

  function addScene(): string {
    const id = generateId()
    gameData.value = {
      ...gameData.value,
      scenes: {
        ...gameData.value.scenes,
        [id]: {
          id,
          videoUrl: '',
          choices: [],
          nextScene: '',
          subtitleUrl: '',
          onEnter: [],
        },
      },
    }
    trigger()
    return id
  }

  function deleteScene(id: string) {
    if (startSceneId.value === id) return
    const nextScenes = { ...gameData.value.scenes }
    delete nextScenes[id]
    for (const key of Object.keys(nextScenes)) {
      const s = nextScenes[key]
      if (s.choices)
        nextScenes[key] = { ...s, choices: s.choices.filter((c) => c.targetScene !== id) }
      if (s.nextScene === id) nextScenes[key] = { ...nextScenes[key], nextScene: '' }
      if (s.qte) {
        const qte = { ...s.qte }
        let changed = false
        if (qte.successScene === id) { qte.successScene = ''; changed = true }
        if (qte.failScene === id) { qte.failScene = ''; changed = true }
        if (changed) nextScenes[key] = { ...nextScenes[key], qte }
      }
    }
    gameData.value = { ...gameData.value, scenes: nextScenes }
    trigger()
    if (selectedNodeId.value === id) selectedNodeId.value = null
  }

  function updateScene(id: string, partial: Partial<SceneNode>) {
    const scene = gameData.value.scenes[id]
    if (!scene) return
    gameData.value = {
      ...gameData.value,
      scenes: { ...gameData.value.scenes, [id]: { ...scene, ...partial } },
    }
    trigger()
  }

  function addChoice(sourceId: string) {
    const scene = gameData.value.scenes[sourceId]
    if (!scene) return
    gameData.value = {
      ...gameData.value,
      scenes: {
        ...gameData.value.scenes,
        [sourceId]: {
          ...scene,
          choices: [...(scene.choices || []), { text: '\u65b0\u9009\u9879', targetScene: '' }],
        },
      },
    }
    trigger()
  }

  function updateChoice(sourceId: string, index: number, partial: Partial<Choice>) {
    const scene = gameData.value.scenes[sourceId]
    if (!scene?.choices) return
    const newChoices = scene.choices.map((c, i) => (i === index ? { ...c, ...partial } : c))
    gameData.value = {
      ...gameData.value,
      scenes: { ...gameData.value.scenes, [sourceId]: { ...scene, choices: newChoices } },
    }
    trigger()
  }

  function deleteChoice(sourceId: string, index: number) {
    const scene = gameData.value.scenes[sourceId]
    if (!scene?.choices) return
    gameData.value = {
      ...gameData.value,
      scenes: {
        ...gameData.value.scenes,
        [sourceId]: { ...scene, choices: scene.choices.filter((_, i) => i !== index) },
      },
    }
    trigger()
  }

  return {
    gameData,
    selectedNodeId,
    selectedScene,
    sceneList,
    sceneNodes,
    sceneEdges,
    startSceneId,
    loadJSON,
    exportJSON,
    addScene,
    deleteScene,
    updateScene,
    addChoice,
    updateChoice,
    deleteChoice,
    generateId,
  }
}
