<template>
  <div class="ref-mgr-page">
    <PluginListPanel ref="pluginPanelRef" :visible="true" @changed="refresh" />

    <div class="ref-mgr-header">
      <h2 class="ref-mgr-title">{{ title }}</h2>
      <div class="ref-mgr-actions">
        <el-input
          v-model="search"
          :placeholder="`搜索 ${kindLabel} (id / 名称 / 类型)`"
          size="small"
          clearable
          class="ref-mgr-search"
        >
          <template #prefix><el-icon><Search /></el-icon></template>
        </el-input>

        <!-- 创建按钮：需要 typeName 选择的（process/car）走下拉；
             其它（site/track/image/text/model）走直接弹表单。
             特例：disableCreate=true 时整个按钮不渲染（脚本管理这种"只读列表"用）。 -->
        <template v-if="!disableCreate">
          <template v-if="needsTypeNamePicker">
            <el-dropdown trigger="click" :disabled="creatableTypes.length === 0" @command="onCreateByTypeName">
              <el-button type="primary" size="small" :icon="Plus" :disabled="creatableTypes.length === 0">
                新建{{ kindLabel }}<el-icon class="el-icon--right"><CaretBottom /></el-icon>
              </el-button>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item
                    v-for="t in creatableTypes"
                    :key="t.typeName"
                    :command="t.typeName"
                  >
                    {{ t.label }}
                    <span class="ref-type-asm">· {{ t.shortName }}</span>
                  </el-dropdown-item>
                  <el-dropdown-item v-if="creatableTypes.length === 0" disabled>
                    未发现可创建的子类型
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </template>
          <template v-else>
            <el-button type="primary" size="small" :icon="Plus" @click="openCreateForm()">
              新建{{ kindLabel }}
            </el-button>
          </template>
        </template>

        <el-tooltip content="重新扫描 ./plugins 目录加载新增 .dll，并重建可创建类型列表" placement="bottom">
          <el-button
            size="small"
            :loading="reloadingPlugins"
            :icon="Connection"
            @click="onReloadPlugins"
          >
            刷新插件
          </el-button>
        </el-tooltip>
        <el-button size="small" :loading="loading" :icon="Refresh" @click="refresh">刷新</el-button>
      </div>
    </div>

    <div class="ref-mgr-body">
      <!-- 左：列表 -->
      <el-card shadow="never" class="ref-mgr-list-card">
        <el-table
          v-loading="loading"
          :data="filteredObjects"
          stripe
          border
          size="small"
          highlight-current-row
          class="ref-mgr-table"
          @current-change="onSelect"
        >
          <el-table-column prop="id" label="ID" width="70" />
          <el-table-column prop="name" label="名称" min-width="160" />
          <el-table-column prop="typeName" label="类型" width="160" show-overflow-tooltip />
          <el-table-column
            v-if="showSummaryColumn"
            prop="summary"
            :label="summaryLabel ?? '概况'"
            min-width="160"
            show-overflow-tooltip
          />
          <el-table-column
            v-if="showStatusColumn"
            prop="status"
            :label="statusLabel ?? '状态'"
            width="120"
            show-overflow-tooltip
          />
          <el-table-column label="操作" :width="rowActionsWidth">
            <template #default="{ row }">
              <el-button
                text
                size="small"
                @click.stop="onHighlight3D(row as ReflectionObject)"
              >
                3D 高亮
              </el-button>
              <!-- 脚本管理专用：与 SimpleLite 工作台「脚本」表格行内按钮组对齐，
                   选不选中都能直接点行内「查看脚本 / 查看异常状态」弹窗。 -->
              <template v-if="showScriptActions && kind === 'script'">
                <el-button
                  text
                  size="small"
                  :loading="scriptActionLoadingForRow(row, 'source')"
                  :disabled="scriptActionLoading !== null"
                  @click.stop="openScriptSourceDialogFor(row as ReflectionObject)"
                >
                  查看脚本
                </el-button>
                <el-button
                  text
                  size="small"
                  :loading="scriptActionLoadingForRow(row, 'status')"
                  :disabled="scriptActionLoading !== null"
                  @click.stop="openScriptStatusDialogFor(row as ReflectionObject)"
                >
                  异常状态
                </el-button>
              </template>
              <el-button
                v-if="!disableDelete"
                text
                size="small"
                type="danger"
                :loading="deleting === row.id"
                @click.stop="onDelete(row as ReflectionObject)"
              >
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-empty
          v-if="!loading && filteredObjects.length === 0"
          :description="emptyText"
        />
      </el-card>

      <!-- 右：详情 -->
      <el-card shadow="never" class="ref-mgr-detail-card">
        <template v-if="!current">
          <el-empty description="左侧选中一行查看 / 编辑详情" />
        </template>
        <template v-else>
          <div class="detail-header">
            <div class="detail-title">
              <el-tag size="small" type="info">{{ current.typeName }}</el-tag>
              <span class="detail-id">#{{ current.id }}</span>
              <span class="detail-name">{{ current.name }}</span>
            </div>
            <div class="detail-actions">
              <button class="header-action-btn" @click="onHighlight3D(current)">在 3D 中高亮</button>
            </div>
          </div>

          <!-- 强类型字段（[FieldMember]） -->
          <div class="field-section">
            <div class="field-section-title">强类型字段 ({{ typedFields.length }})</div>
            <el-descriptions
              v-if="typedFields.length > 0"
              :column="2"
              border
              size="small"
            >
              <el-descriptions-item
                v-for="f in typedFields"
                :key="f.key"
                :label="f.key"
                :label-style="f.locked ? { color: '#aaa' } : undefined"
              >
                <el-switch
                  v-if="isBoolField(f)"
                  :model-value="parseBool(f.value)"
                  :disabled="!!f.locked || savingField === f.key"
                  size="small"
                  @change="(v: any) => onSetField(f.key, v ? 'true' : 'false')"
                />
                <el-color-picker
                  v-else-if="isColorField(f)"
                  :model-value="f.value || '#ffffff'"
                  :disabled="!!f.locked || savingField === f.key"
                  show-alpha
                  size="small"
                  @change="(v: string | null) => onSetField(f.key, v ?? '')"
                />
                <el-input-number
                  v-else-if="isNumberField(f)"
                  :model-value="parseNum(f.value)"
                  :controls="false"
                  :disabled="!!f.locked || savingField === f.key"
                  size="small"
                  class="field-num"
                  @change="(v: number | undefined) => onSetField(f.key, v == null ? '' : String(v))"
                />
                <el-input
                  v-else
                  v-model="f.value"
                  size="small"
                  :disabled="!!f.locked || savingField === f.key"
                  @change="(v: string) => onSetField(f.key, v)"
                />
              </el-descriptions-item>
            </el-descriptions>
            <div v-else class="field-empty">该对象未声明任何 [FieldMember]</div>
          </div>

          <!-- 动态字段（Prop.fields） -->
          <div class="field-section">
            <div class="field-section-title">
              <span>动态字段 ({{ dynamicFields.length }})</span>
              <button class="add-field-btn" @click="onAddField">＋ 添加字段</button>
            </div>
            <el-descriptions
              v-if="dynamicFields.length > 0"
              :column="1"
              border
              size="small"
            >
              <el-descriptions-item
                v-for="f in dynamicFields"
                :key="f.key"
                :label="f.key"
              >
                <div class="dyn-row">
                  <el-input
                    v-model="f.value"
                    size="small"
                    :disabled="savingField === f.key"
                    @change="(v: string) => onSetField(f.key, v)"
                  />
                  <el-tooltip content="删除该字段" placement="top">
                    <el-button
                      size="small"
                      text
                      type="danger"
                      :loading="deletingField === f.key"
                      @click="onDeleteField(f.key)"
                    >
                      删除
                    </el-button>
                  </el-tooltip>
                </div>
              </el-descriptions-item>
            </el-descriptions>
            <div v-else class="field-empty">尚无动态字段；点击右上角「＋ 添加字段」</div>
          </div>

          <!-- 运行状态（只读） -->
          <div v-if="status.length > 0" class="field-section">
            <div class="field-section-title">运行状态 (只读, {{ status.length }})</div>
            <el-descriptions :column="1" border size="small">
              <el-descriptions-item v-for="s in status" :key="s.key" :label="statusItemLabel(s)">
                <span
                  :class="['status-value', { 'status-value--array': isArrayLikeStatus(s) }]"
                  :title="s.typeName"
                >{{ formatStatusDisplay(s) }}</span>
              </el-descriptions-item>
            </el-descriptions>
          </div>

          <!-- 动作 / 方法（紫色渐变按钮，配色与地图编辑栏一致） -->
          <div class="field-section">
            <div class="field-section-title">动作 / 方法 ({{ actionCount }})</div>
            <div v-if="loadingMethods" class="field-empty">加载方法中…</div>
            <template v-else>
              <div v-if="scriptActionsVisible" class="method-grid script-method-grid">
                <el-tooltip content="弹窗展示当前 CarProgram 的完整 script 内容" placement="top">
                  <button
                    class="method-btn"
                    :disabled="scriptActionLoading !== null"
                    @click="openScriptSourceDialog"
                  >
                    <span v-if="scriptActionLoading === 'source'" class="method-btn-spin">⟳</span>
                    <span v-else class="method-btn-icon">▶</span>
                    <span class="method-btn-label">查看脚本</span>
                  </button>
                </el-tooltip>
                <el-tooltip content="弹窗展示 state、exception、notifies 等异常状态报告" placement="top">
                  <button
                    class="method-btn"
                    :disabled="scriptActionLoading !== null"
                    @click="openScriptStatusDialog"
                  >
                    <span v-if="scriptActionLoading === 'status'" class="method-btn-spin">⟳</span>
                    <span v-else class="method-btn-icon">▶</span>
                    <span class="method-btn-label">查看异常状态</span>
                  </button>
                </el-tooltip>
              </div>
              <div v-if="methods.length === 0 && !scriptActionsVisible" class="field-empty">该类型未暴露 [MethodMember]</div>
              <div v-else-if="methods.length > 0" class="method-grid">
                <el-tooltip
                  v-for="m in methods"
                  :key="m.methodName"
                  :content="m.description ?? m.hint ?? ''"
                  :disabled="!m.description && !m.hint"
                  placement="top"
                >
                  <button
                    class="method-btn"
                    :disabled="executing === m.methodName"
                    @click="runMethod(m)"
                  >
                    <span v-if="executing === m.methodName" class="method-btn-spin">⟳</span>
                    <span v-else class="method-btn-icon">▶</span>
                    <span class="method-btn-label">{{ m.label ?? m.methodName }}</span>
                    <span v-if="m.hasParams" class="method-btn-tag">带参</span>
                  </button>
                </el-tooltip>
              </div>
            </template>
          </div>
        </template>
      </el-card>
    </div>

    <!-- 创建对话框：用于 site/track/special 等需要业务参数的 kind。 -->
    <el-dialog
      v-model="createDialogOpen"
      :title="`新建 ${kindLabel}${createDialogSubKind ? ' · ' + subKindLabel(createDialogSubKind) : ''}`"
      width="480px"
      append-to-body
    >
      <el-form
        :model="createForm"
        size="small"
        label-width="100px"
        @submit.prevent="onConfirmCreate"
      >
        <!-- track 类型：要先选 line / bezier / arc / nurbs -->
        <el-form-item v-if="createDialogSubKind === 'track'" label="路径类型">
          <el-radio-group v-model="createForm.trackKind">
            <el-radio-button value="line">直线</el-radio-button>
            <el-radio-button value="bezier">贝塞尔</el-radio-button>
            <el-radio-button value="arc">圆弧</el-radio-button>
            <el-radio-button value="nurbs">NURBS</el-radio-button>
          </el-radio-group>
        </el-form-item>

        <el-form-item label="名称">
          <el-input v-model="createForm.name" placeholder="(可空，留空走默认名)" />
        </el-form-item>
        <el-form-item label="图层">
          <el-input v-model="createForm.layer" placeholder="g / main / ..." />
        </el-form-item>

        <!-- site 坐标 -->
        <template v-if="['site', 'image', 'text', 'model'].includes(createDialogSubKind)">
          <el-form-item label="X (mm)">
            <el-input-number v-model="createForm.x" :controls="false" />
          </el-form-item>
          <el-form-item label="Y (mm)">
            <el-input-number v-model="createForm.y" :controls="false" />
          </el-form-item>
          <el-form-item v-if="createDialogSubKind === 'site'" label="Z (mm)">
            <el-input-number v-model="createForm.z" :controls="false" />
          </el-form-item>
        </template>

        <!-- track 端点 -->
        <template v-if="createDialogSubKind === 'track'">
          <el-form-item label="起点站 ID">
            <el-input-number v-model="createForm.siteA" :controls="false" :min="1" />
          </el-form-item>
          <el-form-item label="终点站 ID">
            <el-input-number v-model="createForm.siteB" :controls="false" :min="1" />
          </el-form-item>
        </template>

        <!-- image / model 路径 -->
        <el-form-item v-if="createDialogSubKind === 'image'" label="图片路径">
          <el-input v-model="createForm.imagePath" placeholder="/uploads/xxxxx.png" />
        </el-form-item>
        <el-form-item v-if="createDialogSubKind === 'model'" label="模型路径">
          <el-input v-model="createForm.modelPath" placeholder="/uploads/xxxxx.glb" />
        </el-form-item>
        <el-form-item v-if="createDialogSubKind === 'text'" label="文本">
          <el-input v-model="createForm.content" placeholder="Text" />
        </el-form-item>

        <!-- car：可选 typeName -->
        <el-form-item v-if="createDialogSubKind === 'car'" label="X (mm)">
          <el-input-number v-model="createForm.x" :controls="false" />
        </el-form-item>
        <el-form-item v-if="createDialogSubKind === 'car'" label="Y (mm)">
          <el-input-number v-model="createForm.y" :controls="false" />
        </el-form-item>
      </el-form>

      <template #footer>
        <el-button @click="createDialogOpen = false">取消</el-button>
        <el-button type="primary" :loading="creating" @click="onConfirmCreate">创建</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="scriptDialogOpen"
      :title="scriptDialogTitle"
      width="720px"
      append-to-body
    >
      <el-input
        :model-value="scriptDialogContent"
        type="textarea"
        :autosize="{ minRows: 16, maxRows: 26 }"
        readonly
        class="script-dialog-textarea"
      />
      <template #footer>
        <el-button @click="copyScriptDialogContent">复制</el-button>
        <el-button type="primary" @click="scriptDialogOpen = false">关闭</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
/**
 * 通用「反射式」对象管理面板：
 *   - 列表：调 reflectionApi.listObjects(kind)
 *   - 详情：调 reflectionApi.getBundle(kind, id)，按 source 分组渲染 typed / dynamic 字段
 *   - 字段编辑：onSetField → reflectionApi.setField；动态字段可一键删除（reflectionApi.deleteField）
 *   - 添加字段：弹 ElMessageBox 输入 key + value，调 setField 落地（后端会 fall through 到 Prop.fields）
 *   - 创建：插件子类（process/script/car-typed）走 dropdown 选 typeName；
 *           site/track/image/text/model 走弹窗表单收集 x/y/siteA/siteB 等
 *   - 删除：reflectionApi.deleteObject
 *   - 方法调用：reflectionApi.execute（带参时弹 prompt 逐项收集）
 *   - 3D 高亮：reflectionApi.setSelection 让 SimpleLite 工作台同步选中
 *
 * SSE 订阅：object-created/deleted/patched/batch-changed 都会自动 refresh。
 */

import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Plus, Refresh, CaretBottom, Connection } from '@element-plus/icons-vue'
import {
  reflectionApi,
  type ReflectionKind,
  type ReflectionMethod,
  type ReflectionObject,
  type ReflectionCreatableType,
  type ReflectionKv
} from '@/api/reflection'
import { useMapEditStream } from '@/composables/useMapEditStream'
import PluginListPanel from './PluginListPanel.vue'

const props = defineProps<{
  kind: ReflectionKind
  /** 单数标签，例如「进程」「脚本」。用于按钮文案 / 搜索框占位。 */
  kindLabel: string
  title: string
  emptyText?: string
  /**
   * 隐藏右上角「新建」按钮 / 下拉。
   * 用于 CarProgram 等"只读"列表 —— 后端不支持手动创建，前端也不应再渲染按钮（即使禁用也容易误点）。
   */
  disableCreate?: boolean
  /**
   * 隐藏每行的「删除」按钮。
   * 同样用于 CarProgram 等 Mission 运行时托管的对象：删除会被后端拒，干脆不渲染。
   */
  disableDelete?: boolean
  /**
   * 在表格中追加「概况」列，绑定 ReflectionObject.summary。脚本管理用来展示 plans[0].usingCar 信息。
   */
  showSummaryColumn?: boolean
  /** 「概况」列表头文案。默认「概况」。 */
  summaryLabel?: string
  /**
   * 在表格中追加「状态」列，绑定 ReflectionObject.status。脚本管理用来展示 ProgramStatus.state。
   */
  showStatusColumn?: boolean
  /** 「状态」列表头文案。默认「状态」。 */
  statusLabel?: string
  /** 脚本管理专用：在动作区追加 SimpleLite 工作台同款「查看脚本 / 查看异常状态」。 */
  showScriptActions?: boolean
}>()

const loading = ref(false)
const loadingMethods = ref(false)
const executing = ref<string | null>(null)
const deleting = ref<number | null>(null)
const deletingField = ref<string | null>(null)
const savingField = ref<string | null>(null)
const reloadingPlugins = ref(false)
const creating = ref(false)
const search = ref('')
const scriptActionLoading = ref<'source' | 'status' | null>(null)
const scriptActionLoadingRowId = ref<number | null>(null)
const scriptDialogOpen = ref(false)
const scriptDialogTitle = ref('')
const scriptDialogContent = ref('')

const pluginPanelRef = ref<InstanceType<typeof PluginListPanel> | null>(null)

const objects = ref<ReflectionObject[]>([])
const creatableTypes = ref<ReflectionCreatableType[]>([])

const current = ref<ReflectionObject | null>(null)
interface MutableKv { key: string; value: string; locked?: boolean; source?: 'typed' | 'dynamic'; typeName?: string }
const allFields = ref<MutableKv[]>([])
const status = ref<ReflectionKv[]>([])
const methods = ref<ReflectionMethod[]>([])

const typedFields = computed(() => allFields.value.filter((f) => f.source !== 'dynamic'))
const dynamicFields = computed(() => allFields.value.filter((f) => f.source === 'dynamic'))
const scriptActionsVisible = computed(() => !!props.showScriptActions && props.kind === 'script' && !!current.value)
const actionCount = computed(() => methods.value.length + (scriptActionsVisible.value ? 2 : 0))

// 「操作」列宽度：3D 高亮(70) + 可选行内查看脚本(80) + 异常状态(80) + 可选删除(60) + 间距。
// 三种典型组合：
//   - 仅 3D 高亮：110
//   - 3D 高亮 + 删除：170
//   - 3D 高亮 + 查看脚本 + 异常状态（脚本管理 disableDelete）：240
const rowActionsWidth = computed(() => {
  let w = 110
  if (props.showScriptActions && props.kind === 'script') w += 130
  if (!props.disableDelete) w += 60
  return w
})

const filteredObjects = computed(() => {
  const q = search.value.trim().toLowerCase()
  if (!q) return objects.value
  return objects.value.filter((o) =>
    String(o.id).includes(q) ||
    (o.name ?? '').toLowerCase().includes(q) ||
    (o.typeName ?? '').toLowerCase().includes(q)
  )
})

// process / car-typed 需要先在下拉里挑插件子类型，再实例化；
// site / track / image / text / model 直接弹表单收坐标和图层。
const PICKER_KINDS = new Set<ReflectionKind>(['process', 'mission', 'car', 'vehicle'])
const needsTypeNamePicker = computed(() => PICKER_KINDS.has(props.kind))

async function refresh() {
  loading.value = true
  try {
    const [list, types] = await Promise.all([
      reflectionApi.listObjects(props.kind),
      reflectionApi.listCreatableTypes(props.kind).catch(() => [])
    ])
    objects.value = list
    creatableTypes.value = types
    if (current.value && !objects.value.find((o) => o.id === current.value!.id)) {
      current.value = null
      allFields.value = []
      methods.value = []
      status.value = []
    }
  } catch (err) {
    ElMessage.error(`加载 ${props.kindLabel} 失败：${(err as Error).message}`)
  } finally {
    loading.value = false
  }
}

async function onSelect(row: ReflectionObject | null) {
  current.value = row
  allFields.value = []
  methods.value = []
  status.value = []
  if (!row) return
  loadingMethods.value = true
  try {
    // 注意：scene 合成 kind 下的行带 subKind，bundle/execute/setField 必须走子 kind
    // 否则后端 ResolveObject 拿不到对象。
    const realKind = (row.subKind as ReflectionKind | undefined) ?? props.kind
    const b = await reflectionApi.getBundle(realKind, row.id)
    if (b.fieldList && b.fieldList.length > 0) {
      // 新版后端：直接吐 source/typeName 标记
      allFields.value = b.fieldList.map((kv) => ({ ...kv }))
    } else {
      // 兼容旧版后端：从 fields 还原，全部标 dynamic，让用户至少能改
      allFields.value = Object.entries(b.fields ?? {}).map(([k, v]) => ({
        key: k, value: String(v), source: 'dynamic'
      }))
    }
    methods.value = b.methods ?? []
    status.value = b.status ?? []
  } catch (err) {
    ElMessage.error(`加载详情失败：${(err as Error).message}`)
  } finally {
    loadingMethods.value = false
  }
}

function resolveKindForRow(row: ReflectionObject | null): ReflectionKind {
  return ((row?.subKind as ReflectionKind | undefined) ?? props.kind)
}

async function onReloadPlugins() {
  reloadingPlugins.value = true
  try {
    const r = await reflectionApi.reloadPlugins()
    ElMessage.success(
      `已扫描 plugins/ 目录：发现 ${r.totalDlls} dll，新加载 ${r.newlyLoaded}；` +
      `进程类型 ${r.missionTypes}、车辆/脚本类型 ${r.carTypes}`
    )
    await refresh()
    pluginPanelRef.value?.refresh()
  } catch (err) {
    ElMessage.error(`刷新插件失败：${(err as Error).message}`)
  } finally {
    reloadingPlugins.value = false
  }
}

// ──────────────────────────────────────────────────────────────────────────
// 创建：两条入口
//   1) typename 下拉 → onCreateByTypeName(typeName)
//   2) 弹表单 → openCreateForm() → onConfirmCreate
// ──────────────────────────────────────────────────────────────────────────

const createDialogOpen = ref(false)
const createDialogSubKind = ref<string>('')
const createForm = reactive<{
  name: string
  layer: string
  x: number
  y: number
  z: number
  siteA: number
  siteB: number
  trackKind: 'line' | 'bezier' | 'arc' | 'nurbs'
  imagePath: string
  modelPath: string
  content: string
}>({
  name: '',
  layer: '',
  x: 0,
  y: 0,
  z: 0,
  siteA: 0,
  siteB: 0,
  trackKind: 'line',
  imagePath: '',
  modelPath: '',
  content: 'Text'
})

function subKindLabel(sk: string): string {
  switch (sk) {
    case 'site': return '站点'
    case 'track': return '路径'
    case 'image': return '图片'
    case 'text': return '文本'
    case 'model': return '模型'
    case 'car': return '车辆'
    default: return sk
  }
}

/**
 * 打开创建对话框。如果传 subKind 则强制按该子类创建（用于 scene 多 subKind 场景）；
 * 否则按 props.kind 推断（site 直接 site；track 直接 track；car 直接 car；
 * special 弹 image 表单——其它装饰物子类后续可继续扩 createDialogSubKind）。
 */
function openCreateForm(subKind?: string) {
  const sk = subKind ?? (props.kind === 'special' ? 'image'
    : props.kind === 'scene' ? 'site'
    : props.kind === 'vehicle' ? 'car'
    : props.kind)
  createDialogSubKind.value = sk
  createForm.name = ''
  createForm.layer = ''
  createForm.x = 0
  createForm.y = 0
  createForm.z = 0
  createForm.siteA = 0
  createForm.siteB = 0
  createForm.trackKind = 'line'
  createForm.imagePath = ''
  createForm.modelPath = ''
  createForm.content = 'Text'
  createDialogOpen.value = true
}

/** 由父组件主动调用（例如 SceneManagerView 的 subKind tab 下「新建」按钮）。 */
defineExpose({ openCreateForm, refresh })

async function onConfirmCreate() {
  creating.value = true
  try {
    const sk = createDialogSubKind.value
    let kindToPost: ReflectionKind = sk as ReflectionKind
    const extras: Record<string, string | number | boolean | undefined> = {
      name: createForm.name || undefined,
      layer: createForm.layer || undefined
    }
    if (sk === 'site') {
      extras.x = createForm.x
      extras.y = createForm.y
      extras.z = createForm.z
    } else if (sk === 'track') {
      kindToPost = createForm.trackKind === 'line' ? 'track' : (createForm.trackKind as ReflectionKind)
      extras.siteA = createForm.siteA
      extras.siteB = createForm.siteB
    } else if (sk === 'image') {
      extras.x = createForm.x
      extras.y = createForm.y
      extras.imagePath = createForm.imagePath
    } else if (sk === 'text') {
      extras.x = createForm.x
      extras.y = createForm.y
      extras.content = createForm.content
    } else if (sk === 'model') {
      extras.x = createForm.x
      extras.y = createForm.y
      extras.modelPath = createForm.modelPath
    } else if (sk === 'car') {
      extras.x = createForm.x
      extras.y = createForm.y
    }
    const created = await reflectionApi.createObject(kindToPost, '', extras)
    ElMessage.success(`已创建 ${created.typeName ?? sk} #${created.id}`)
    createDialogOpen.value = false
    await refresh()
    const row = objects.value.find((o) => o.id === created.id)
    if (row) await onSelect(row)
  } catch (err) {
    ElMessage.error(`创建失败：${(err as Error).message}`)
  } finally {
    creating.value = false
  }
}

async function onCreateByTypeName(typeName: string) {
  try {
    let name: string | undefined
    try {
      const r = await ElMessageBox.prompt(
        `为新${props.kindLabel}指定名称（可空，留空使用默认名）`,
        `新建${props.kindLabel}`,
        { inputValue: '', confirmButtonText: '创建', cancelButtonText: '取消' }
      )
      name = r.value || undefined
    } catch {
      return
    }
    const created = await reflectionApi.createObject(props.kind, typeName, { name })
    ElMessage.success(`已创建 ${created.typeName} #${created.id}`)
    await refresh()
    const row = objects.value.find((o) => o.id === created.id)
    if (row) await onSelect(row)
  } catch (err) {
    ElMessage.error(`创建失败：${(err as Error).message}`)
  }
}

async function onDelete(row: ReflectionObject) {
  try {
    await ElMessageBox.confirm(
      `确认删除 ${row.typeName} #${row.id} (${row.name})？该操作无法撤销。`,
      `删除${props.kindLabel}`,
      { type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
    )
  } catch {
    return
  }
  deleting.value = row.id
  try {
    const realKind = resolveKindForRow(row)
    await reflectionApi.deleteObject(realKind, row.id)
    ElMessage.success(`已删除 ${row.typeName} #${row.id}`)
    if (current.value?.id === row.id) current.value = null
    await refresh()
  } catch (err) {
    ElMessage.error(`删除失败：${(err as Error).message}`)
  } finally {
    deleting.value = null
  }
}

async function onSetField(key: string, value: string) {
  if (!current.value) return
  savingField.value = key
  try {
    const realKind = resolveKindForRow(current.value)
    await reflectionApi.setField(realKind, current.value.id, key, value)
    ElMessage.success(`已更新 ${key}`)
  } catch (err) {
    ElMessage.error(`更新 ${key} 失败：${(err as Error).message}`)
    await onSelect(current.value)
  } finally {
    savingField.value = null
  }
}

async function onDeleteField(key: string) {
  if (!current.value) return
  try {
    await ElMessageBox.confirm(`确认删除动态字段 "${key}"？`, '删除字段',
      { type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' })
  } catch { return }
  deletingField.value = key
  try {
    const realKind = resolveKindForRow(current.value)
    await reflectionApi.deleteField(realKind, current.value.id, key)
    ElMessage.success(`已删除字段 ${key}`)
    await onSelect(current.value)
  } catch (err) {
    ElMessage.error(`删除字段失败：${(err as Error).message}`)
  } finally {
    deletingField.value = null
  }
}

async function onAddField() {
  if (!current.value) return
  let key = ''
  try {
    const r = await ElMessageBox.prompt('字段名（key）', '添加动态字段', {
      inputValidator: (v) => !!v && /^[A-Za-z_][A-Za-z0-9_\-]*$/.test(v) || '只允许字母/数字/下划线/横线，且首字符非数字',
      confirmButtonText: '下一步', cancelButtonText: '取消'
    })
    key = r.value
  } catch { return }
  let value = ''
  try {
    const r = await ElMessageBox.prompt(`字段 "${key}" 的初始值（可空）`, '添加动态字段', {
      inputValue: '', confirmButtonText: '保存', cancelButtonText: '取消'
    })
    value = r.value
  } catch { return }
  if (allFields.value.find((f) => f.key === key)) {
    ElMessage.warning(`字段 ${key} 已存在，请直接编辑`)
    return
  }
  await onSetField(key, value)
  await onSelect(current.value)
}

async function onHighlight3D(row: ReflectionObject | null) {
  if (!row) return
  try {
    const realKind = resolveKindForRow(row)
    await reflectionApi.setSelection(realKind, row.id)
    ElMessage.success(`已在 3D 视口高亮 ${row.typeName ?? realKind} #${row.id}`)
  } catch (err) {
    ElMessage.error(`高亮失败：${(err as Error).message}`)
  }
}

async function openScriptSourceDialogFor(target: ReflectionObject) {
  if (!target) return
  scriptActionLoading.value = 'source'
  scriptActionLoadingRowId.value = target.id
  try {
    const r = await reflectionApi.getScriptSource(target.id)
    scriptDialogTitle.value = `脚本 ${r.name || r.typeName}(#${r.id}) 内容`
    scriptDialogContent.value = r.script || '(无脚本)'
    scriptDialogOpen.value = true
  } catch (err) {
    ElMessage.error(`查看脚本失败：${(err as Error).message}`)
  } finally {
    scriptActionLoading.value = null
    scriptActionLoadingRowId.value = null
  }
}

async function openScriptStatusDialogFor(target: ReflectionObject) {
  if (!target) return
  scriptActionLoading.value = 'status'
  scriptActionLoadingRowId.value = target.id
  try {
    const r = await reflectionApi.getScriptExceptionStatus(target.id)
    scriptDialogTitle.value = `脚本 ${r.name || r.typeName}(#${r.id}) 异常状态`
    scriptDialogContent.value = r.report || '(无异常状态)'
    scriptDialogOpen.value = true
  } catch (err) {
    ElMessage.error(`查看异常状态失败：${(err as Error).message}`)
  } finally {
    scriptActionLoading.value = null
    scriptActionLoadingRowId.value = null
  }
}

// 详情面板「动作 / 方法」区域的两个按钮：以右侧 current 行为目标。
// 与表格行内按钮共用 *For 实现，避免逻辑漂移。
function openScriptSourceDialog() {
  if (current.value) openScriptSourceDialogFor(current.value)
}
function openScriptStatusDialog() {
  if (current.value) openScriptStatusDialogFor(current.value)
}

// 表格行内按钮独立 loading：仅当前点击的行才转圈，其余按钮 disable 但不显示 spinner。
function scriptActionLoadingForRow(row: ReflectionObject, kind: 'source' | 'status'): boolean {
  return scriptActionLoadingRowId.value === row.id && scriptActionLoading.value === kind
}

async function copyScriptDialogContent() {
  try {
    await navigator.clipboard.writeText(scriptDialogContent.value)
    ElMessage.success('已复制到剪贴板')
  } catch {
    ElMessage.warning('复制失败，请手动选择文本复制')
  }
}

async function runMethod(m: ReflectionMethod) {
  if (!current.value) return
  const realKind = resolveKindForRow(current.value)
  if (m.hasParams) {
    const params: Record<string, string> = {}
    for (const p of m.params) {
      try {
        const r = await ElMessageBox.prompt(
          `参数 ${p.name} (${p.typeName})` + (p.hasDefault ? `\n默认: ${p.defaultValue}` : ''),
          `调用 ${m.label ?? m.methodName}`,
          { inputValue: p.defaultValue ?? '' }
        )
        params[p.name] = r.value
      } catch {
        return
      }
    }
    executing.value = m.methodName
    try {
      const r = await reflectionApi.execute(realKind, current.value.id, m.methodName, params)
      ElMessage.success(`${m.label ?? m.methodName} 完成${r.returnValue ? `：${r.returnValue}` : ''}`)
      await onSelect(current.value)
    } catch (err) {
      ElMessage.error(`执行失败：${(err as Error).message}`)
    } finally {
      executing.value = null
    }
    return
  }
  executing.value = m.methodName
  try {
    const r = await reflectionApi.execute(realKind, current.value.id, m.methodName)
    ElMessage.success(`${m.label ?? m.methodName} 完成${r.returnValue ? `：${r.returnValue}` : ''}`)
    await onSelect(current.value)
  } catch (err) {
    ElMessage.error(`执行失败：${(err as Error).message}`)
  } finally {
    executing.value = null
  }
}

// ──────────────────────────────────────────────────────────────────────────
// 字段类型推断（用于挑控件）
// ──────────────────────────────────────────────────────────────────────────

function isBoolField(f: MutableKv): boolean {
  if (f.typeName === 'Boolean') return true
  return f.value === 'true' || f.value === 'false' || f.value === 'True' || f.value === 'False'
}
function isColorField(f: MutableKv): boolean {
  if (f.key === 'color' || f.key === 'textColor') return true
  return /^#[0-9a-fA-F]{3,8}$/.test(f.value)
}
function isNumberField(f: MutableKv): boolean {
  if (!f.typeName) return false
  return ['Int32', 'Int64', 'Single', 'Double', 'Decimal', 'UInt32', 'UInt64'].includes(f.typeName)
}
function parseBool(v: string): boolean {
  return v === 'true' || v === 'True' || v === '1'
}
function parseNum(v: string): number | undefined {
  if (v == null || v === '') return undefined
  const n = Number(v)
  return Number.isFinite(n) ? n : undefined
}

function statusItemLabel(s: ReflectionKv): string {
  const tn = s.typeName?.trim()
  return tn && tn !== 'String' ? `${s.key}  (${tn})` : s.key
}

function formatStatusDisplay(s: ReflectionKv): string {
  const v = (s.value ?? '').trim()
  if (v) return v
  return s.typeName ? `（空 · ${s.typeName}）` : '（空）'
}

function isArrayLikeStatus(s: ReflectionKv): boolean {
  const tn = s.typeName ?? ''
  if (tn.endsWith('[]')) return true
  const v = (s.value ?? '').trim()
  return v.startsWith('[') || v.startsWith('{')
}

// SSE 订阅：后端落地 / 删除 / patch 后自动刷新
useMapEditStream({
  onObjectCreated: () => refresh(),
  onObjectDeleted: () => refresh(),
  onObjectPatched: () => {
    if (current.value) onSelect(current.value)
  },
  onObjectBatchChanged: () => refresh()
})

watch(() => props.kind, refresh)
onMounted(refresh)
</script>

<style scoped>
.ref-mgr-page {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  color: var(--mg-text-light);
}
.ref-mgr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0 4px;
  border-bottom: 1px solid var(--mg-divider);
}
.ref-mgr-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  /* 用 CSS 变量自适应深色 / 浅色主题，#fff 兜底深色玻璃语境。 */
  color: var(--el-text-color-primary, #fff);
  letter-spacing: 0.5px;
}
.ref-mgr-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ref-mgr-search { width: 280px; }

.ref-type-asm {
  margin-left: 6px;
  color: var(--mg-text-dim);
  font-size: 11px;
}

.ref-mgr-body {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(380px, 0.9fr) 1.4fr;
  gap: 12px;
  min-height: 0;
}

.ref-mgr-list-card,
.ref-mgr-detail-card {
  background: rgba(30, 14, 55, 0.55) !important;
  border: 1px solid rgba(255, 255, 255, 0.08) !important;
  backdrop-filter: blur(12px);
  height: 100%;
  display: flex;
  flex-direction: column;
}
.ref-mgr-list-card :deep(.el-card__body),
.ref-mgr-detail-card :deep(.el-card__body) {
  flex: 1;
  overflow: auto;
  padding: 12px;
}
.ref-mgr-table { flex: 1; min-height: 240px; }

.detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px dashed var(--mg-veil-border);
}
.detail-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.detail-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.detail-id {
  color: var(--mg-text-dim);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12.5px;
}
.detail-name {
  color: var(--el-text-color-primary, #fff);
  font-size: 15px;
  font-weight: 600;
}

.field-section {
  margin-top: 14px;
}
.field-section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 600;
  /* CSS 变量自适应深 / 浅主题，避免白底白字。 */
  color: var(--el-text-color-primary, rgba(255, 255, 255, 0.85));
  margin-bottom: 8px;
  letter-spacing: 0.4px;
}
.field-empty {
  color: var(--mg-text-dim);
  font-size: 12px;
  padding: 6px 2px;
}
.status-value {
  display: inline-block;
  max-width: 100%;
  word-break: break-word;
  line-height: 1.45;
}
.status-value--array {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  color: var(--mg-text-muted);
}
.method-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
}
.script-method-grid {
  margin-bottom: 8px;
}

/* 动作按钮：紫色渐变 + 白色文字，hover 时加亮 + 阴影。
   配色参考 EditPropertyPanel 的 .prop-action-btn 与 EditTopBar 的 .topbar-btn。 */
.method-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 8px 12px;
  border-radius: 7px;
  background: linear-gradient(135deg, rgba(170, 110, 250, 0.30) 0%, rgba(120, 70, 220, 0.30) 100%);
  border: 1px solid rgba(170, 110, 250, 0.55);
  color: #fff;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.3px;
  display: flex;
  align-items: center;
  gap: 6px;
  text-align: left;
  transition: background .14s ease, border-color .14s ease, transform .12s ease, box-shadow .14s ease;
}
.method-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, rgba(170, 110, 250, 0.72) 0%, rgba(255, 110, 220, 0.62) 100%);
  border-color: rgba(255, 130, 220, 0.78);
  box-shadow: 0 4px 14px rgba(180, 90, 220, 0.42);
}
.method-btn:active:not(:disabled) { transform: scale(0.985); }
.method-btn:disabled { opacity: 0.55; cursor: progress; }
.method-btn-icon {
  font-size: 10px;
  color: rgba(255, 200, 250, 0.85);
  flex: none;
}
.method-btn-spin {
  display: inline-block;
  font-size: 13px;
  animation: ref-spin .8s linear infinite;
}
.method-btn-label {
  flex: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.method-btn-tag {
  flex: none;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.18);
  font-size: 10px;
  letter-spacing: 0.4px;
}
@keyframes ref-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* 「+ 添加字段」按钮：与动作按钮同色系但偏淡，区分等级。 */
.add-field-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 4px 10px;
  border-radius: 6px;
  background: rgba(170, 110, 250, 0.18);
  border: 1px dashed rgba(170, 110, 250, 0.45);
  color: rgba(255, 230, 255, 0.95);
  font-size: 12px;
  font-weight: 500;
  transition: background .14s ease, border-color .14s ease, color .14s ease;
}
.add-field-btn:hover {
  background: rgba(170, 110, 250, 0.34);
  border-color: rgba(170, 110, 250, 0.75);
  color: #fff;
}

/* 详情头「在 3D 中高亮」按钮：与动作按钮同款样式。 */
.header-action-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 6px 14px;
  border-radius: 6px;
  background: linear-gradient(135deg, rgba(170, 110, 250, 0.32) 0%, rgba(120, 70, 220, 0.32) 100%);
  border: 1px solid rgba(170, 110, 250, 0.55);
  color: #fff;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.4px;
  transition: background .14s ease, border-color .14s ease, box-shadow .14s ease;
}
.header-action-btn:hover {
  background: linear-gradient(135deg, rgba(170, 110, 250, 0.72) 0%, rgba(255, 110, 220, 0.62) 100%);
  border-color: rgba(255, 130, 220, 0.78);
  box-shadow: 0 3px 10px rgba(180, 90, 220, 0.40);
}

.script-dialog-textarea :deep(.el-textarea__inner) {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  line-height: 1.45;
}

.dyn-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.field-num :deep(.el-input-number) { width: 100%; }

:deep(.el-descriptions__title) {
  color: var(--mg-text-light) !important;
  font-size: 13px;
  font-weight: 600;
}
:deep(.el-descriptions__label) { color: var(--mg-text-muted); }
:deep(.el-descriptions__content) { color: var(--mg-text-light); }
:deep(.el-table) {
  background: transparent;
}
:deep(.el-table__inner-wrapper),
:deep(.el-table tr),
:deep(.el-table th.el-table__cell),
:deep(.el-table td.el-table__cell) {
  background: transparent !important;
  color: var(--mg-text-light);
  border-color: var(--mg-divider);
}
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) {
  background: var(--mg-veil-1) !important;
}
:deep(.el-table__body tr.current-row > td.el-table__cell) {
  background: rgba(170, 110, 250, 0.20) !important;
}

/* ── 浅紫主题：卡片变白后，半透明渐变动作按钮上的白字会看不清，
   改为实色紫底白字（对比 ~6.9:1，过 WCAG AA）。scoped 限本组件，不影响地图编辑画布。 ── */
:root[data-theme="fame-lavender"] .method-btn,
:root[data-theme="fame-lavender"] .header-action-btn {
  background: #7c3aed;
  border-color: #7c3aed;
  color: #fff;
}
:root[data-theme="fame-lavender"] .method-btn:hover:not(:disabled),
:root[data-theme="fame-lavender"] .header-action-btn:hover {
  background: #6d28d9;
  border-color: #6d28d9;
  box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);
}
:root[data-theme="fame-lavender"] .method-btn-icon { color: rgba(255, 255, 255, 0.85); }
:root[data-theme="fame-lavender"] .method-btn-tag { background: rgba(255, 255, 255, 0.28); }
:root[data-theme="fame-lavender"] .add-field-btn {
  background: rgba(124, 58, 237, 0.12);
  border-color: rgba(124, 58, 237, 0.5);
  color: #6d28d9;
}
:root[data-theme="fame-lavender"] .add-field-btn:hover {
  background: rgba(124, 58, 237, 0.22);
  border-color: rgba(124, 58, 237, 0.7);
  color: #5b21b6;
}
</style>
