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

    <div class="ref-mgr-header">
      <div class="ref-mgr-heading">
        <h2 class="ref-mgr-title">{{ shortTitle }}</h2>
        <span class="ref-mgr-count">{{ filteredObjects.length }} / {{ objects.length }}</span>
      </div>
      <div class="ref-mgr-actions">
        <el-input
          v-model="search"
          :placeholder="`搜索 ${kindLabel}`"
          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>
        <el-tooltip
          v-if="showProjectSaveButton"
          :content="projectSaveTooltip"
          placement="bottom"
        >
          <el-button
            size="small"
            type="primary"
            :loading="savingProject"
            :icon="Document"
            @click="onSaveProject"
          >
            {{ saveButtonLabel }}
          </el-button>
        </el-tooltip>
      </div>
    </div>

    <div v-if="typeChips.length > 1" class="ref-mgr-chips" role="tablist">
      <button
        type="button"
        class="ref-chip"
        :class="{ 'is-active': !typeFilter }"
        @click="typeFilter = ''"
      >
        全部<b>{{ objects.length }}</b>
      </button>
      <button
        v-for="chip in typeChips"
        :key="chip.type"
        type="button"
        class="ref-chip"
        :class="{ 'is-active': typeFilter === chip.type }"
        @click="typeFilter = typeFilter === chip.type ? '' : chip.type"
      >
        {{ chip.short }}<b>{{ chip.count }}</b>
      </button>
    </div>

    <div class="ref-mgr-body">
      <!-- 左：列表 -->
      <el-card shadow="never" class="ref-mgr-list-card">
        <el-table
          ref="tableRef"
          v-loading="loading"
          :data="filteredObjects"
          row-key="id"
          stripe
          size="small"
          highlight-current-row
          class="ref-mgr-table"
          @current-change="onSelect"
        >
          <el-table-column prop="id" label="ID" width="72">
            <template #default="{ row }">
              <span class="col-mono">{{ row.id }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="name" label="名称" min-width="140" show-overflow-tooltip />
          <el-table-column prop="typeName" label="类型" width="140" show-overflow-tooltip>
            <template #default="{ row }">
              <span class="col-type">{{ shortTypeName(row.typeName) }}</span>
            </template>
          </el-table-column>
          <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
                v-if="viewInMapEnabled"
                text
                size="small"
                @click.stop="onViewInMap(row as ReflectionObject)"
              >
                查看对象
              </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 v-if="viewInMapEnabled" class="detail-actions">
              <button class="header-action-btn" @click="onViewInMap(current)">查看对象</button>
            </div>
          </div>

          <el-tabs v-model="detailTab" class="detail-tabs">
            <el-tab-pane label="属性" name="properties" />
            <el-tab-pane label="状态" name="status" />
            <el-tab-pane label="动作" name="action" />
          </el-tabs>

          <div class="detail-tab-body">
            <template v-if="detailTab === 'properties'">
          <div class="field-section field-section--first">
            <div class="field-section-title"><span>基础属性</span><b>{{ typedFields.length }}</b></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>动态字段</span><b>{{ dynamicFields.length }}</b>
              <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>
            </template>

            <template v-else-if="detailTab === 'status'">
          <div class="field-section field-section--first">
            <div class="field-section-title">运行状态 (只读, {{ status.length }})</div>
            <el-descriptions v-if="status.length > 0" :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 v-else class="field-empty">暂无运行状态信息</div>
          </div>
            </template>

            <template v-else>
          <div class="field-section field-section--first">
            <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>
          </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, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox, type TableInstance } from 'element-plus'
import { Search, Plus, Refresh, CaretBottom, Connection, Document } from '@element-plus/icons-vue'
import {
  reflectionApi,
  formatReflectionExecuteMessage,
  type ReflectionKind,
  type ReflectionMethod,
  type ReflectionObject,
  type ReflectionCreatableType,
  type ReflectionKv
} from '@/api/reflection'
import { useMapEditStream } from '@/composables/useMapEditStream'
import type { DetailTab } from '@/types/workbench'
import {
  buildMapFocusQuery,
  isMapViewableKind,
  MAP_MONITOR_PATH,
  toMapFocusKind
} from '@/utils/mapObjectFocus'
import { isProjectPersistableKind } from '@/utils/projectPersistence'
import { executeProjectSave, promptSaveMode } from '@/utils/projectSaveFlow'
import PluginListPanel from './PluginListPanel.vue'

const router = useRouter()

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
  /**
   * 列表状态列取自运行状态反射中的 key（如进程 Mission 的 status）。
   */
  statusReflectionKey?: string
  /** 脚本管理专用：在动作区追加 SimpleLite 工作台同款「查看脚本 / 查看异常状态」。 */
  showScriptActions?: boolean
  /** 增删改后是否自动写回项目 JSON。默认车辆/场景/进程启用。 */
  autoSaveProject?: boolean
  /** 是否展示「保存」按钮；默认车辆/场景/进程显示。 */
  enableProjectSave?: boolean
  /** 保存按钮文案，默认「保存」。 */
  saveButtonLabel?: string
}>()

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 savingProject = ref(false)
const lastProjectSavePath = ref<string | null>(null)
const creating = ref(false)
const search = ref('')
const typeFilter = 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 tableRef = ref<TableInstance | null>(null)
const detailLoadingForId = ref<number | null>(null)

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

const current = ref<ReflectionObject | null>(null)
const detailTab = ref<DetailTab>('properties')
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))
const viewInMapEnabled = computed(() => isMapViewableKind(props.kind))
const showProjectSaveButton = computed(
  () => props.enableProjectSave ?? isProjectPersistableKind(props.kind)
)
const autoSaveProjectEnabled = computed(
  () => props.autoSaveProject ?? isProjectPersistableKind(props.kind)
)
const saveButtonLabel = computed(() => props.saveButtonLabel ?? '保存')
const shortTitle = computed(() => {
  const t = (props.title || '').trim()
  const cut = t.indexOf('（')
  return cut > 0 ? t.slice(0, cut) : t
})
const projectSaveTooltip = computed(() => {
  if (lastProjectSavePath.value) {
    return `将当前内存项目写回 JSON；上次保存：${lastProjectSavePath.value}`
  }
  return '将场景/车辆/进程等修改写回项目 JSON，下次启动可自动加载'
})

const rowActionsWidth = computed(() => {
  let w = 0
  if (viewInMapEnabled.value) w += 90
  if (props.showScriptActions && props.kind === 'script') w += 130
  if (!props.disableDelete) w += 60
  return Math.max(w, 72)
})

function shortTypeName(typeName?: string | null): string {
  const raw = (typeName ?? '').trim()
  if (!raw) return '—'
  const parts = raw.split(/[.+]/)
  return parts[parts.length - 1] || raw
}

const typeChips = computed(() => {
  const map = new Map<string, number>()
  for (const o of objects.value) {
    const t = o.typeName || 'Unknown'
    map.set(t, (map.get(t) ?? 0) + 1)
  }
  return [...map.entries()]
    .map(([type, count]) => ({ type, short: shortTypeName(type), count }))
    .sort((a, b) => b.count - a.count || a.short.localeCompare(b.short))
    .slice(0, 8)
})

const filteredObjects = computed(() => {
  const q = search.value.trim().toLowerCase()
  return objects.value.filter((o) => {
    if (typeFilter.value && o.typeName !== typeFilter.value) return false
    if (!q) return true
    return (
      String(o.id).includes(q) ||
      (o.name ?? '').toLowerCase().includes(q) ||
      (o.typeName ?? '').toLowerCase().includes(q) ||
      (o.summary ?? '').toLowerCase().includes(q) ||
      (o.status ?? '').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))

function pickReflectionStatusValue(rows: ReflectionKv[], key: string): string | null {
  const hit = rows.find((r) => r.key === key)
  const v = hit?.value?.trim()
  return v ? v : null
}

async function enrichObjectsWithReflectionStatus(list: ReflectionObject[]): Promise<ReflectionObject[]> {
  const key = props.statusReflectionKey
  if (!props.showStatusColumn || !key || list.length === 0) return list
  // 限并发：对象较多时（如进程/库位上百条）避免一次性发起 N 个 status 请求压垮浏览器连接池与后端，
  // 用固定大小的 worker 池逐个领取任务；result[i] 按下标回填，保持与入参一致的顺序。
  const CONCURRENCY = 6
  const result = list.slice()
  let cursor = 0
  const worker = async () => {
    while (cursor < list.length) {
      const i = cursor++
      const o = list[i]
      try {
        const realKind = (o.subKind as ReflectionKind | undefined) ?? props.kind
        const rows = await reflectionApi.getStatus(realKind, o.id)
        const v = pickReflectionStatusValue(rows, key)
        if (v) result[i] = { ...o, status: v }
      } catch { /* keep list row */ }
    }
  }
  await Promise.all(Array.from({ length: Math.min(CONCURRENCY, list.length) }, worker))
  return result
}

function syncListStatusFromBundle(rowId: number, statusRows: ReflectionKv[]) {
  const key = props.statusReflectionKey
  if (!props.showStatusColumn || !key) return
  const v = pickReflectionStatusValue(statusRows, key)
  if (!v) return
  const idx = objects.value.findIndex((o) => o.id === rowId)
  if (idx < 0) return
  if (objects.value[idx].status !== v) objects.value[idx].status = v
}

function restoreTableCurrentRow(rowId: number | null | undefined) {
  if (rowId == null) return
  const row = objects.value.find((o) => o.id === rowId)
  if (!row) return
  void nextTick(() => tableRef.value?.setCurrentRow(row))
}

async function refresh() {
  loading.value = true
  const preservedId = current.value?.id ?? null
  try {
    const [list, types] = await Promise.all([
      reflectionApi.listObjects(props.kind),
      reflectionApi.listCreatableTypes(props.kind).catch(() => [])
    ])
    objects.value = await enrichObjectsWithReflectionStatus(list)
    creatableTypes.value = types
    const preserved = preservedId != null ? objects.value.find((o) => o.id === preservedId) : undefined
    if (preserved) {
      current.value = preserved
      restoreTableCurrentRow(preserved.id)
    } else if (preservedId != null) {
      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) {
  if (!row) {
    if (detailLoadingForId.value != null) return
    current.value = null
    allFields.value = []
    methods.value = []
    status.value = []
    return
  }
  // 仅在切换到「不同对象」时重置回属性页；同一对象的 SSE patch / refresh 重选要保留用户当前所在标签（状态/动作）。
  const isSameRow = current.value?.id === row.id
  current.value = row
  if (!isSameRow) detailTab.value = 'properties'
  allFields.value = []
  methods.value = []
  status.value = []
  detailLoadingForId.value = row.id
  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 ?? []
    syncListStatusFromBundle(row.id, status.value)
    restoreTableCurrentRow(row.id)
  } catch (err) {
    ElMessage.error(`加载详情失败：${(err as Error).message}`)
  } finally {
    loadingMethods.value = false
    detailLoadingForId.value = null
  }
}

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

async function saveProjectToDisk(opts?: { quiet?: boolean }): Promise<boolean> {
  if (!showProjectSaveButton.value && !autoSaveProjectEnabled.value) return true
  if (savingProject.value) return false
  savingProject.value = true
  try {
    const r = await reflectionApi.saveProject()
    lastProjectSavePath.value = r.path
    if (!opts?.quiet) {
      ElMessage.success({ message: `项目已保存：${r.path}`, grouping: true })
    }
    return true
  } catch (err) {
    ElMessage.warning({
      message: `保存项目到本地失败：${(err as Error).message}`,
      grouping: true
    })
    return false
  } finally {
    savingProject.value = false
  }
}

async function persistProjectAfterMutation() {
  if (!autoSaveProjectEnabled.value) return
  const ok = await saveProjectToDisk({ quiet: true })
  if (!ok) {
    ElMessage.warning({
      message: `变更已生效于内存，但未能写入项目文件，请稍后点击「${saveButtonLabel.value}」重试`,
      grouping: true,
      duration: 5000
    })
  }
}

async function onSaveProject() {
  const mode = await promptSaveMode()
  if (mode === 'cancel') return
  if (savingProject.value) return
  savingProject.value = true
  try {
    const path = await executeProjectSave(mode)
    if (path) {
      lastProjectSavePath.value = path
      ElMessage.success({ message: `项目已保存：${path}`, grouping: true })
    }
  } catch (err) {
    ElMessage.warning({
      message: `保存项目到本地失败：${(err as Error).message}`,
      grouping: true
    })
  } finally {
    savingProject.value = false
  }
}

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)
    await persistProjectAfterMutation()
  } 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)
    await persistProjectAfterMutation()
  } 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()
    await persistProjectAfterMutation()
  } 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}`)
    await persistProjectAfterMutation()
  } 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)
    await persistProjectAfterMutation()
  } 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 onViewInMap(row: ReflectionObject | null) {
  if (!row || !viewInMapEnabled.value) return
  const realKind = resolveKindForRow(row)
  const focusKind = toMapFocusKind(realKind)
  if (!focusKind) return
  try {
    await router.push({
      path: MAP_MONITOR_PATH,
      query: buildMapFocusQuery(focusKind, 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(formatReflectionExecuteMessage(m.label ?? m.methodName, r))
      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(formatReflectionExecuteMessage(m.label ?? m.methodName, r))
    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, () => {
  typeFilter.value = ''
  search.value = ''
  void refresh()
})
onMounted(refresh)
</script>

<style scoped>
.ref-mgr-page {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--mg-text-light);
  min-height: 0;
}
.ref-mgr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 40px;
  padding: 4px 2px 8px;
  border-bottom: 1px solid var(--mg-veil-border, var(--mg-divider));
  flex: none;
}
.ref-mgr-heading {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.ref-mgr-title {
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  color: var(--el-text-color-primary, #fff);
  letter-spacing: 0.2px;
  white-space: nowrap;
}
.ref-mgr-count {
  font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--mg-text-muted, #8a7aa8);
}
.ref-mgr-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.ref-mgr-search { width: 220px; }

.ref-mgr-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: none;
}
.ref-chip {
  appearance: none;
  border: 1px solid var(--mg-veil-border, rgba(124, 58, 237, 0.18));
  background: rgba(var(--mg-bg-card-rgb, 255, 255, 255), 0.92);
  color: var(--mg-text-muted, #756d85);
  border-radius: 6px;
  padding: 3px 10px;
  font-size: 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.ref-chip b {
  font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  color: var(--el-text-color-primary, #2d1b69);
}
.ref-chip:hover { color: var(--el-text-color-primary, #2d1b69); }
.ref-chip.is-active {
  color: var(--mg-primary, #7c3aed);
  border-color: rgba(124, 58, 237, 0.45);
  background: rgba(124, 58, 237, 0.08);
}

.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(420px, 1.15fr) minmax(320px, 0.95fr);
  gap: 10px;
  min-height: 0;
}

.ref-mgr-list-card,
.ref-mgr-detail-card {
  background: rgba(var(--mg-bg-card-rgb, 255, 255, 255), 0.94) !important;
  border: 1px solid var(--mg-veil-border, rgba(124, 58, 237, 0.14)) !important;
  border-radius: 8px !important;
  box-shadow: none !important;
  backdrop-filter: none;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ref-mgr-list-card :deep(.el-card__body),
.ref-mgr-detail-card :deep(.el-card__body) {
  flex: 1;
  overflow: auto;
  padding: 8px 10px;
  min-height: 0;
}
.ref-mgr-table { flex: 1; min-height: 200px; width: 100%; }
.col-mono {
  font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--mg-text-muted, #756d85);
}
.col-type {
  font-size: 12px;
  color: var(--mg-text-muted, #756d85);
}

.detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--mg-veil-border, var(--mg-divider));
}
.detail-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.detail-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.detail-id {
  color: var(--mg-text-dim);
  font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}
.detail-name {
  color: var(--el-text-color-primary, #fff);
  font-size: 14px;
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.detail-tabs :deep(.el-tabs__header) { margin-bottom: 0; }
.detail-tabs :deep(.el-tabs__nav-wrap::after) { height: 1px; }
.detail-tabs :deep(.el-tabs__item) {
  color: var(--mg-text-muted) !important;
  height: 34px;
  line-height: 34px;
  font-size: 13px;
  padding: 0 14px !important;
}
.detail-tabs :deep(.el-tabs__item.is-active) {
  color: var(--mg-primary, #7c3aed) !important;
  font-weight: 650;
}
.detail-tabs :deep(.el-tabs__active-bar) {
  background: var(--mg-primary, #7c3aed);
  height: 2px;
}
.detail-tab-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding-top: 8px;
}

.field-section {
  margin-top: 12px;
}
.field-section--first {
  margin-top: 0;
}
.field-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 650;
  color: var(--el-text-color-primary, rgba(255, 255, 255, 0.85));
  margin-bottom: 8px;
  letter-spacing: 0.3px;
  padding-left: 8px;
  border-left: 2px solid var(--mg-primary, #7c3aed);
}
.field-section-title b {
  font-family: var(--mg-font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  color: var(--mg-text-muted, #756d85);
  margin-right: auto;
}
.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;
}

/* Style A：实色主按钮，去掉霓虹渐变。 */
.method-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 7px 10px;
  border-radius: 6px;
  background: var(--mg-primary, #7c3aed);
  border: 1px solid var(--mg-primary, #7c3aed);
  color: #fff;
  font-size: 12px;
  font-weight: 550;
  letter-spacing: 0.2px;
  display: flex;
  align-items: center;
  gap: 6px;
  text-align: left;
  transition: background .12s ease, border-color .12s ease, transform .1s ease;
}
.method-btn:hover:not(:disabled) {
  background: var(--mg-primary-hover, #6d28d9);
  border-color: var(--mg-primary-hover, #6d28d9);
}
.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, 255, 255, 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;
  cursor: pointer;
  padding: 3px 8px;
  border-radius: 6px;
  background: transparent;
  border: 1px dashed rgba(124, 58, 237, 0.45);
  color: var(--mg-primary, #7c3aed);
  font-size: 12px;
  font-weight: 550;
  transition: background .12s ease, border-color .12s ease;
  margin-left: auto;
}
.add-field-btn:hover {
  background: rgba(124, 58, 237, 0.08);
  border-color: rgba(124, 58, 237, 0.7);
}

.header-action-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 5px 12px;
  border-radius: 6px;
  background: var(--mg-primary, #7c3aed);
  border: 1px solid var(--mg-primary, #7c3aed);
  color: #fff;
  font-size: 12px;
  font-weight: 550;
  letter-spacing: 0.2px;
  transition: background .12s ease;
}
.header-action-btn:hover {
  background: var(--mg-primary-hover, #6d28d9);
  border-color: var(--mg-primary-hover, #6d28d9);
}

.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(124, 58, 237, 0.10) !important;
}
:deep(.el-table .el-button.is-text) {
  padding: 2px 6px;
}

@media (max-width: 1100px) {
  .ref-mgr-body { grid-template-columns: 1fr; }
  .ref-mgr-search { width: min(220px, 100%); }
}
</style>
