<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { GameData, SceneNode } from '@engine/types'
import { useGraphEditor } from './composables/useGraphEditor'
import SceneGraph from './components/SceneGraph.vue'
import NodeEditor from './components/NodeEditor.vue'
import PreviewPanel from './components/PreviewPanel.vue'

const editor = useGraphEditor()
const dirty = ref(false)
const fileInputRef = ref<HTMLInputElement | null>(null)
const loading = ref(true)
const error = ref('')

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

const previewVideoUrl = computed(() => {
  const url = selectedScene.value?.videoUrl
  if (!url) return null
  return url.startsWith('/') ? url : '/' + url
})

function newNode() {
  const id = editor.addScene()
  editor.selectedNodeId.value = id
  dirty.value = true
}

function delNode(id: string) {
  editor.deleteScene(id)
  dirty.value = true
}

function onUpdateScene(id: string, partial: any) {
  editor.updateScene(id, partial)
  dirty.value = true
}

function onAddChoice(sourceId: string) {
  editor.addChoice(sourceId)
  dirty.value = true
}

function onUpdateChoice(sourceId: string, index: number, partial: any) {
  editor.updateChoice(sourceId, index, partial)
  dirty.value = true
}

function onDeleteChoice(sourceId: string, index: number) {
  editor.deleteChoice(sourceId, index)
  dirty.value = true
}

function onAddEdge(source: string, target: string) {
  const scene = editor.gameData.value.scenes[source]
  if (!scene) return
  const newChoices = [...(scene.choices || []), { text: `${source} → ${target}`, targetScene: target }]
  editor.gameData.value = {
    ...editor.gameData.value,
    scenes: { ...editor.gameData.value.scenes, [source]: { ...scene, choices: newChoices } },
  }
  dirty.value = true
}

function exportJSON() {
  const data = editor.exportJSON()
  const json = JSON.stringify(data, null, 2)
  const blob = new Blob([json], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'scenes.json'
  a.click()
  URL.revokeObjectURL(url)
  dirty.value = false
}

function importJSON() {
  fileInputRef.value?.click()
}

async function onFileSelected(e: Event) {
  const file = (e.target as HTMLInputElement).files?.[0]
  if (!file) return
  try {
    const data = JSON.parse(await file.text()) as GameData
    if (!data.scenes || typeof data.scenes !== 'object') throw new Error('无效的场景数据')
    editor.loadJSON(data)
    dirty.value = false
  } catch (err: any) {
    error.value = '导入失败：' + err.message
    setTimeout(() => (error.value = ''), 3000)
  }
}

async function loadDemo() {
  try {
    loading.value = true
    const resp = await fetch('/scenes/demo.json')
    if (!resp.ok) throw new Error('HTTP ' + resp.status)
    editor.loadJSON(await resp.json())
    dirty.value = false
  } catch (err: any) {
    error.value = '加载示例失败：' + err.message
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  loadDemo()
})
</script>

<template>
  <div class="editor-layout">
    <div class="toolbar">
      <span class="toolbar-title">剧情编辑器</span>
      <div class="toolbar-actions">
        <button @click="newNode">+ 新场景</button>
        <button @click="importJSON" class="secondary">导入 JSON</button>
        <button @click="exportJSON" class="secondary">导出 JSON</button>
        <button @click="loadDemo" class="secondary">加载示例</button>
        <span v-if="dirty" class="dirty-indicator">● 未保存</span>
      </div>
      <span class="toolbar-start">
        起始场景:
        <select
          :value="editor.startSceneId.value"
          @change="editor.startSceneId.value = ($event.target as HTMLSelectElement).value; dirty = true"
          class="start-select"
        >
          <option value="">-- 选择 --</option>
          <option v-for="n in editor.sceneNodes.value" :key="n.id" :value="n.id">{{ n.label }}</option>
        </select>
      </span>
    </div>

    <div v-if="error" class="error-bar">{{ error }}</div>

    <div class="editor-main">
      <SceneGraph
        v-if="!loading"
        class="graph-area"
        :scene-nodes="editor.sceneNodes.value"
        :scene-edges="editor.sceneEdges.value"
        :start-scene="editor.startSceneId.value"
        :selected-node-id="editor.selectedNodeId.value"
        @select-node="editor.selectedNodeId.value = $event"
        @add-edge="onAddEdge"
      />

      <div v-else class="graph-area loading-hint">加载剧情数据…</div>

      <NodeEditor
        :scene="selectedScene"
        :scene-list="editor.sceneList.value"
        @update="onUpdateScene"
        @add-choice="onAddChoice"
        @update-choice="onUpdateChoice"
        @delete-choice="onDeleteChoice"
        @delete-scene="delNode"
        @close="editor.selectedNodeId.value = null"
      />

      <PreviewPanel :video-url="previewVideoUrl" />
    </div>

    <input ref="fileInputRef" type="file" accept=".json" style="display:none" @change="onFileSelected" />
  </div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #0a0a16;
  color: #ccc;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

#editor-app {
  width: 100%;
  height: 100%;
}
</style>

<style scoped>
.editor-layout {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: #111122;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}

.toolbar-title {
  font-size: 15px;
  font-weight: 600;
  color: #ddd;
  letter-spacing: 1px;
  margin-right: 16px;
}

.toolbar-actions {
  display: flex;
  gap: 8px;
}

.toolbar-actions button {
  padding: 6px 14px;
  font-size: 12px;
  color: #ddd;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.15s;
}

.toolbar-actions button:hover { background: rgba(255,255,255,0.14); }

.toolbar-actions button.secondary {
  color: #8cf;
  border-color: rgba(100,200,255,0.2);
  background: rgba(100,200,255,0.06);
}

.toolbar-start {
  margin-left: auto;
  font-size: 12px;
  color: #777;
  display: flex;
  align-items: center;
  gap: 6px;
}

.start-select {
  padding: 4px 8px;
  font-size: 12px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 3px;
  color: #ccc;
}

.dirty-indicator {
  font-size: 11px;
  color: #ff9800;
}

.error-bar {
  padding: 8px 16px;
  background: #4a1a1a;
  color: #f88;
  font-size: 13px;
  flex-shrink: 0;
}

.editor-main {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.graph-area {
  flex: 1;
}

.loading-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #555;
  font-size: 14px;
}
</style>
