<template>
  <div class="designer">
    <header class="toolbar">
      <div class="left">
        <el-select v-model="templateId" size="small" style="width: 220px" @change="loadTemplate">
          <el-option v-for="t in world.state.templates" :key="t.id" :label="t.name" :value="t.id" />
        </el-select>
        <el-input v-model="draft.name" size="small" class="name" placeholder="模板名称" />
        <el-tag size="small" :type="draft.published ? 'success' : 'info'">
          {{ draft.published ? '已发布' : '草稿' }}
        </el-tag>
      </div>
      <div class="center">
        <el-tag v-for="(iss, i) in issues.slice(0, 3)" :key="i" size="small" :type="iss.level === 'error' ? 'danger' : 'warning'">
          {{ iss.message }}
        </el-tag>
        <el-tag v-if="!issues.length" size="small" type="success">校验通过</el-tag>
      </div>
      <div class="right">
        <el-button size="small" @click="reloadFromWorld">从库重载</el-button>
        <el-button size="small" type="primary" plain @click="saveDraft">保存到原型库</el-button>
        <el-button size="small" type="primary" @click="publish">发布</el-button>
        <el-button size="small" @click="openJson = !openJson">{{ openJson ? '隐藏 JSON' : '高级 JSON' }}</el-button>
      </div>
    </header>

    <div class="body">
      <aside class="panel left-panel">
        <div class="panel-title">模板列表</div>
        <el-menu :default-active="templateId" class="tpl-menu" @select="onSelectMenu">
          <el-menu-item v-for="t in world.state.templates" :key="t.id" :index="t.id">
            <span>{{ t.name }}</span>
            <el-tag size="small" class="pri">P{{ t.meta.priority }}</el-tag>
          </el-menu-item>
        </el-menu>
        <div class="panel-title">控件库</div>
        <div class="palette-wrap">
          <WcsPalette v-model:side="dropSide" />
        </div>
      </aside>

      <main class="panel center-panel">
        <VueFlow
          v-model:nodes="nodes"
          v-model:edges="edges"
          :node-types="nodeTypes"
          fit-view-on-init
          :default-viewport="{ x: 40, y: 20, zoom: 0.85 }"
          @drop="onDrop"
          @dragover="onDragOver"
          @node-click="onNodeClick"
          @pane-click="selectedId = null"
        >
          <Background pattern-color="#e8eaed" :gap="18" />
          <Controls position="bottom-right" />
        </VueFlow>
        <div class="lane-hint">左列=源策略 · 右列=宿策略 · 中轴=触发/绑定/预占/动作</div>
      </main>

      <aside class="panel right-panel">
        <div class="panel-title">属性配置</div>
        <WcsNodeInspector
          :tpl="draft"
          :node="selectedNode"
          @change="onInspectorChange"
          @remove-focus="onRemoveFocus"
        />
      </aside>
    </div>

    <WcsTrialPanel :run-trial="runTrial" />

    <el-drawer v-model="openJson" title="高级：DSL JSON" size="40%">
      <el-input v-model="jsonText" type="textarea" :rows="28" class="mono" />
      <div class="drawer-actions">
        <el-button @click="applyJson">从 JSON 应用到画布</el-button>
      </div>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { computed, markRaw, nextTick, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import {
  VueFlow,
  type Edge,
  type Node,
  type NodeMouseEvent,
  type NodeTypesObject
} from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import '@vue-flow/controls/dist/style.css'

import type { TaskTemplateDsl, TrialResult } from '../types'
import type { WcsProtoWorld } from '../runtime/world'
import { validateTemplate } from '../engine/validate'
import {
  appendDroppedNode,
  dslToFlow,
  refreshNodeSummaries,
  type WcsFlowNode
} from './flowBridge'
import type { WcsNodeType } from './nodeCatalog'
import WcsFlowNodeView from './WcsFlowNode.vue'
import WcsPalette from './WcsPalette.vue'
import WcsNodeInspector from './WcsNodeInspector.vue'
import WcsTrialPanel from './WcsTrialPanel.vue'

const props = defineProps<{
  world: WcsProtoWorld
}>()

const nodeTypes = { wcs: markRaw(WcsFlowNodeView) } as NodeTypesObject

const templateId = ref(props.world.state.templates[0]?.id ?? '')
const draft = ref<TaskTemplateDsl>(
  JSON.parse(JSON.stringify(props.world.state.templates[0])) as TaskTemplateDsl
)
/** 用宽松 Node[] 避免 Vue Flow 泛型实例化过深 */
const nodes = ref<Node[]>([])
const edges = ref<Edge[]>([])
const selectedId = ref<string | null>(null)
const dropSide = ref<'source' | 'target'>('source')
const openJson = ref(false)
const jsonText = ref('')

const selectedNode = computed((): WcsFlowNode | null => {
  const n = nodes.value.find((x) => x.id === selectedId.value)
  return n ? (n as unknown as WcsFlowNode) : null
})
const issues = computed(() => validateTemplate(draft.value, 'save'))

function rebuildFlow(focusId?: string) {
  const flow = dslToFlow(draft.value)
  nodes.value = flow.nodes as unknown as Node[]
  edges.value = flow.edges
  if (focusId) {
    nextTick(() => {
      selectedId.value = focusId
    })
  }
}

function loadTemplate() {
  const t = props.world.state.templates.find((x) => x.id === templateId.value)
  if (!t) return
  draft.value = JSON.parse(JSON.stringify(t)) as TaskTemplateDsl
  selectedId.value = null
  rebuildFlow()
  jsonText.value = JSON.stringify(draft.value, null, 2)
}

function onSelectMenu(id: string) {
  templateId.value = id
  loadTemplate()
}

function reloadFromWorld() {
  loadTemplate()
  ElMessage.success('已从原型库重载')
}

function saveDraft() {
  draft.value.published = false
  const r = props.world.saveTemplate(JSON.parse(JSON.stringify(draft.value)) as TaskTemplateDsl)
  if (!r.ok) {
    ElMessage.error(r.issues.map((i) => i.message).join('；'))
    return
  }
  templateId.value = draft.value.id
  ElMessage.success('已保存到原型库')
}

function publish() {
  const rSave = props.world.saveTemplate(JSON.parse(JSON.stringify(draft.value)) as TaskTemplateDsl)
  if (!rSave.ok) {
    ElMessage.error(rSave.issues.map((i) => i.message).join('；'))
    return
  }
  const r = props.world.publishTemplate(draft.value.id)
  if (!r.ok) {
    ElMessage.error(r.issues.filter((i) => i.level === 'error').map((i) => i.message).join('；'))
    return
  }
  draft.value.published = true
  ElMessage.success('发布成功')
}

function onDragOver(e: DragEvent) {
  e.preventDefault()
  if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'
}

function onDrop(e: DragEvent) {
  e.preventDefault()
  const type = e.dataTransfer?.getData('application/wcs-node') as WcsNodeType | undefined
  if (!type) return
  const side = (e.dataTransfer?.getData('application/wcs-side') as 'source' | 'target') || dropSide.value
  const { tpl, focusNodeId } = appendDroppedNode(draft.value, type, side)
  draft.value = tpl
  rebuildFlow(focusNodeId)
  ElMessage.success(`已添加：${type}`)
}

function onNodeClick(ev: NodeMouseEvent) {
  selectedId.value = ev.node.id
}

function onInspectorChange() {
  const refreshed = refreshNodeSummaries(draft.value, nodes.value as unknown as WcsFlowNode[])
  nodes.value = refreshed as unknown as Node[]
  jsonText.value = JSON.stringify(draft.value, null, 2)
}

function onRemoveFocus() {
  selectedId.value = null
  rebuildFlow()
}

function runTrial(payload: Record<string, unknown>): TrialResult {
  // 试算用当前画布草稿（未必已保存）
  const backup = props.world.state.templates.find((t) => t.id === draft.value.id)
  const idx = props.world.state.templates.findIndex((t) => t.id === draft.value.id)
  const snap = JSON.parse(JSON.stringify(draft.value)) as TaskTemplateDsl
  if (idx >= 0) props.world.state.templates[idx] = snap
  else props.world.state.templates.push(snap)
  try {
    return props.world.trial(draft.value.id, payload)
  } finally {
    if (backup && idx >= 0) props.world.state.templates[idx] = backup
  }
}

function applyJson() {
  try {
    const parsed = JSON.parse(jsonText.value) as TaskTemplateDsl
    draft.value = parsed
    templateId.value = draft.value.id
    rebuildFlow()
    ElMessage.success('已应用到画布')
  } catch (e) {
    ElMessage.error(`JSON 无效: ${e}`)
  }
}

watch(openJson, (v) => {
  if (v) jsonText.value = JSON.stringify(draft.value, null, 2)
})

loadTemplate()

defineExpose({ loadTemplate, templateId })
</script>

<style scoped>
.designer {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: var(--el-bg-color-page);
}
.toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--el-border-color);
  background: var(--el-bg-color);
  flex-wrap: wrap;
}
.left,
.right,
.center {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.center {
  flex: 1;
  justify-content: center;
}
.name {
  width: 200px;
}
.body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 240px 1fr 300px;
}
.panel {
  min-height: 0;
  background: var(--el-bg-color);
  display: flex;
  flex-direction: column;
}
.left-panel {
  border-right: 1px solid var(--el-border-color);
}
.right-panel {
  border-left: 1px solid var(--el-border-color);
}
.center-panel {
  position: relative;
  background: #f7f8fa;
}
.panel-title {
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 600;
  border-bottom: 1px solid var(--el-border-color-lighter);
  color: var(--el-text-color-regular);
}
.tpl-menu {
  border-right: none;
  max-height: 160px;
  overflow: auto;
}
.tpl-menu .pri {
  margin-left: 6px;
}
.palette-wrap {
  flex: 1;
  min-height: 0;
}
.lane-hint {
  position: absolute;
  left: 12px;
  top: 8px;
  z-index: 2;
  font-size: 11px;
  color: var(--el-text-color-secondary);
  background: rgba(255, 255, 255, 0.85);
  padding: 2px 8px;
  border-radius: 4px;
  pointer-events: none;
}
.mono :deep(textarea) {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
}
.drawer-actions {
  margin-top: 12px;
}
</style>
