<template>
  <div class="selection-detail">
    <template v-if="!selection">
      <div class="empty muted">未选择对象。在左侧列表或 3D 视图中选中后，此处显示属性 / 状态 / 动作。</div>
    </template>
    <template v-else>
      <div class="head">
        <span class="name">{{ detail?.name ?? selection.name ?? selection.id }}</span>
        <el-tag size="small" type="info">{{ detail?.typeName ?? selection.kind }}</el-tag>
        <el-tooltip v-if="!reflectionKind" content="该对象类型未接入反射可见性过滤，所有内容按默认展示" placement="top">
          <el-tag size="small" type="warning" effect="plain">未过滤</el-tag>
        </el-tooltip>
      </div>

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

      <div v-loading="loading" class="detail-body">
        <template v-if="activeTab === 'properties'">
          <el-descriptions v-if="filteredSummary.length" :column="1" size="small" border title="概要">
            <el-descriptions-item v-for="f in filteredSummary" :key="f.key" :label="f.key">
              {{ f.value }}
            </el-descriptions-item>
          </el-descriptions>
          <div v-if="filteredMemberFields.length" class="section">
            <div class="section-title">成员字段</div>
            <el-table :data="filteredMemberFields" size="small" stripe>
              <el-table-column prop="key" label="字段" width="100" />
              <el-table-column prop="value" label="值" show-overflow-tooltip />
              <el-table-column label="" width="48">
                <template #default="{ row }">
                  <el-tag v-if="row.locked" size="small" type="info">锁定</el-tag>
                </template>
              </el-table-column>
            </el-table>
          </div>
          <div v-if="filteredCustomFields.length" class="section">
            <div class="section-title">自定义字段</div>
            <el-table :data="filteredCustomFields" size="small" stripe>
              <el-table-column prop="key" label="字段" width="100" />
              <el-table-column prop="value" label="值" show-overflow-tooltip />
            </el-table>
          </div>
          <div
            v-if="!filteredSummary.length && !filteredMemberFields.length && !filteredCustomFields.length && fieldWhitelistActive"
            class="muted empty">
            当前白名单未匹配到任何字段。可在「配置中心 → 运营维护（优先）」或「地图监控配置（回退）」放宽白名单。
          </div>
        </template>

        <template v-else-if="activeTab === 'status'">
          <el-descriptions v-if="filteredStatusFields.length" :column="1" size="small" border>
            <el-descriptions-item
              v-for="f in filteredStatusFields"
              :key="f.key"
              :label="f.key">
              {{ f.value }}
            </el-descriptions-item>
          </el-descriptions>
          <div v-else-if="statusWhitelistActive" class="muted empty">
            当前白名单未匹配到任何状态项。可在「配置中心 → 运营维护（优先）」或「地图监控配置（回退）」放宽白名单。
          </div>
          <div v-else class="muted empty">无状态信息。</div>
        </template>

        <template v-else>
          <div v-if="actionsLoading" class="muted empty">加载动作列表中…</div>
          <div v-else-if="!filteredActions.length" class="muted empty">
            {{ methodWhitelistActive
              ? '当前白名单未匹配到任何动作。可在「配置中心 → 运营维护（优先）」或「地图监控配置（回退）」放宽白名单。'
              : '当前对象无可调用动作（MethodMember）。' }}
          </div>
          <div v-else class="action-list">
            <el-button
              v-for="m in filteredActions"
              :key="m.methodName"
              size="small"
              type="primary"
              plain
              class="action-btn"
              :loading="executing === m.methodName"
              @click="onExecute(m)">
              {{ m.label || m.methodName }}
              <el-tooltip v-if="m.description" :content="m.description" placement="top">
                <el-icon class="hint-icon"><InfoFilled /></el-icon>
              </el-tooltip>
            </el-button>
          </div>
        </template>
      </div>
    </template>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { InfoFilled } from '@element-plus/icons-vue'
import { getSelectionDetail } from '@/api/workbench'
import { getConfig } from '@/api/config'
import {
  reflectionApi,
  type ReflectionKv,
  type ReflectionKind,
  type ReflectionMethod
} from '@/api/reflection'
import type { MonitorPanelPolicy, OpsConfig } from '@/types/config'
import type { DetailTab, SelectedObjectRef, SelectionDetail, DetailField } from '@/types/workbench'

const props = defineProps<{
  selection: SelectedObjectRef | null
  refreshKey?: number
}>()

const activeTab = ref<DetailTab>('properties')
const detail = ref<SelectionDetail | null>(null)
const loading = ref(false)

// 反射方法（动作 Tab 用） —— 与 workbench detail 互补：detail.actions 只有 label 没有
// methodName / params，无法真的调 execute；统一切到 reflection.listMethods 路径，可拿到完整签名。
const actions = ref<ReflectionMethod[]>([])
const actionsLoading = ref(false)
const executing = ref<string | null>(null)
const currentCarTypeKey = ref('')

const opsMonitor = ref<Record<'car' | 'site' | 'track', MonitorPanelPolicy> | null>(null)
const opsCarActionByType = ref<Record<string, string[]>>({})
const opsMonitorAt = ref<number>(0)
const OPS_MONITOR_TTL_MS = 30_000

// kind 映射：selection.kind 在前端用 'vehicle' / 'site' / 'track' / 'car' 等，统一映成
// reflection 后端 kind（'car' / 'site' / 'track'）。返回 null 表示该 kind 不在反射 / 监控
// 配置覆盖范围（例如 'image' / 'text' / 'model'），过滤逻辑会自动跳过白名单。
const SELECTION_KIND_TO_REFLECTION: Record<string, ReflectionKind> = {
  car: 'car',
  vehicle: 'car',
  site: 'site',
  track: 'track'
}

const reflectionKind = computed<ReflectionKind | null>(() => {
  const k = props.selection?.kind?.toLowerCase()
  return k ? SELECTION_KIND_TO_REFLECTION[k] ?? null : null
})

const opsPolicy = computed<MonitorPanelPolicy | null>(() => {
  const rk = reflectionKind.value
  if (!rk || !opsMonitor.value) return null
  if (rk === 'car' || rk === 'site' || rk === 'track') {
    return opsMonitor.value[rk]
  }
  return null
})

const activeFieldWhitelist = computed<string[]>(() => {
  return []
})
const activeStatusWhitelist = computed<string[]>(() => {
  return []
})
const activeMethodWhitelist = computed(() => {
  if (reflectionKind.value === 'car') {
    const exact = currentCarTypeKey.value ? opsCarActionByType.value[currentCarTypeKey.value] ?? [] : []
    if (exact.length > 0) return exact
    const short = detail.value?.typeName ?? ''
    const fallback = short ? opsCarActionByType.value[short] ?? [] : []
    if (fallback.length > 0) return fallback
    return []
  }
  return opsPolicy.value?.actionKeys ?? []
})

const fieldWhitelistActive = computed(() => activeFieldWhitelist.value.length > 0)
const statusWhitelistActive = computed(() => activeStatusWhitelist.value.length > 0)
const methodWhitelistActive = computed(() => activeMethodWhitelist.value.length > 0)

function applyFieldFilter<T extends DetailField>(rows: T[] | undefined): T[] {
  if (!rows?.length) return []
  if (!fieldWhitelistActive.value) return rows
  const allow = new Set(activeFieldWhitelist.value)
  return rows.filter((r) => allow.has(r.key))
}

const filteredSummary = computed(() => applyFieldFilter(detail.value?.summary))
const filteredMemberFields = computed(() => applyFieldFilter(detail.value?.memberFields))
const filteredCustomFields = computed(() => applyFieldFilter(detail.value?.customFields))

const filteredStatusFields = computed(() => {
  if (!detail.value) return [] as DetailField[]
  // status Tab 原逻辑：summary + statusLines 合并展示。保留此语义，但分别按白名单过滤。
  const combined: DetailField[] = [
    ...(detail.value.summary ?? []),
    ...(detail.value.statusLines ?? [])
  ]
  if (!statusWhitelistActive.value) return combined
  const allow = new Set(activeStatusWhitelist.value)
  return combined.filter((r) => allow.has(r.key))
})

const filteredActions = computed(() => {
  if (!actions.value.length) return []
  if (!methodWhitelistActive.value) return actions.value
  const allow = new Set(activeMethodWhitelist.value)
  return actions.value.filter((m) => allow.has(m.methodName))
})

async function ensureOpsMonitorConfig() {
  const now = Date.now()
  if (opsMonitor.value && now - opsMonitorAt.value < OPS_MONITOR_TTL_MS) return
  try {
    const env = await getConfig<OpsConfig>('ops')
    const monitor = env.payload?.monitor
    opsMonitor.value = {
      car: monitor?.car ?? { propertyKeys: [], statusKeys: [], actionKeys: [] },
      site: monitor?.site ?? { propertyKeys: [], statusKeys: [], actionKeys: [] },
      track: monitor?.track ?? { propertyKeys: [], statusKeys: [], actionKeys: [] }
    }
    opsCarActionByType.value = monitor?.carActionByType ?? {}
    opsMonitorAt.value = now
  } catch {
    if (!opsMonitor.value) {
      opsMonitor.value = {
        car: { propertyKeys: [], statusKeys: [], actionKeys: [] },
        site: { propertyKeys: [], statusKeys: [], actionKeys: [] },
        track: { propertyKeys: [], statusKeys: [], actionKeys: [] }
      }
      opsCarActionByType.value = {}
    }
  }
}

async function loadDetail() {
  if (!props.selection) {
    detail.value = null
    return
  }
  const rk = reflectionKind.value
  const idNum = Number(props.selection.id)
  loading.value = true
  try {
    if (rk && Number.isFinite(idNum)) {
      if (activeTab.value === 'properties') {
        const bundle = await reflectionApi.getBundle(rk, idNum)
        if (rk === 'car') {
          currentCarTypeKey.value = ('fullTypeName' in bundle && bundle.fullTypeName) ? bundle.fullTypeName : bundle.typeName
        }
        const list = bundle.fieldList ?? []
        const typed = list.filter((x) => x.source !== 'dynamic')
        const dynamic = list.filter((x) => x.source === 'dynamic')
        detail.value = {
          objectKind: rk,
          objectId: String(idNum),
          name: bundle.summary?.name ?? props.selection.name ?? String(idNum),
          typeName: bundle.typeName ?? bundle.summary?.typeName ?? rk,
          layer: bundle.summary?.layer ?? undefined,
          tab: activeTab.value,
          summary: toDetailFields([
            { key: 'ID', value: String(bundle.id) },
            { key: '类型', value: bundle.typeName ?? rk },
            ...(bundle.summary?.layer ? [{ key: '图层', value: bundle.summary.layer }] : [])
          ]),
          memberFields: toDetailFields(typed),
          customFields: toDetailFields(dynamic),
          statusLines: [],
          actions: []
        }
        return
      }
      if (activeTab.value === 'status') {
        const rows = await reflectionApi.getStatus(rk, idNum)
        detail.value = {
          objectKind: rk,
          objectId: String(idNum),
          name: detail.value?.name ?? props.selection.name ?? String(idNum),
          typeName: detail.value?.typeName ?? rk,
          layer: detail.value?.layer,
          tab: activeTab.value,
          summary: detail.value?.summary ?? [],
          memberFields: [],
          customFields: [],
          statusLines: toDetailFields(rows),
          actions: []
        }
        return
      }
    }
    detail.value = await getSelectionDetail(
      props.selection.kind,
      props.selection.id,
      activeTab.value
    )
  } catch (e) {
    detail.value = null
    ElMessage.error(`加载选中信息失败：${(e as Error).message}`)
  } finally {
    loading.value = false
  }
}

async function loadActions() {
  // 只在 reflection 能覆盖的 kind 才拉；其他 kind（image / text / model）保留旧行为：
  // 走 workbench.detail.actions 兜底（label only）—— 通过把 ReflectionMethod 的 methodName
  // 设为空、按钮点击给 ElMessage.info 提示。
  if (!props.selection) {
    actions.value = []
    return
  }
  const rk = reflectionKind.value
  if (!rk) {
    actions.value = (detail.value?.actions ?? []).map((a) => ({
      methodName: '',
      label: a.label,
      description: null,
      hint: null,
      returnType: 'void',
      hasParams: false,
      params: []
    }))
    return
  }
  const idNum = Number(props.selection.id)
  if (!Number.isFinite(idNum)) {
    actions.value = []
    return
  }
  actionsLoading.value = true
  try {
    // 先拉一次 bundle，确保 detail.typeName 对车种动作过滤可用（按车型白名单）。
    if (rk === 'car' && (!detail.value?.typeName || detail.value.typeName === 'car')) {
      try {
        const bundle = await reflectionApi.getBundle(rk, idNum)
        currentCarTypeKey.value = ('fullTypeName' in bundle && bundle.fullTypeName) ? bundle.fullTypeName : bundle.typeName
        detail.value = {
          objectKind: rk,
          objectId: String(idNum),
          name: detail.value?.name ?? bundle.summary?.name ?? props.selection.name ?? String(idNum),
          typeName: bundle.typeName ?? detail.value?.typeName ?? 'car',
          layer: detail.value?.layer ?? bundle.summary?.layer ?? undefined,
          tab: detail.value?.tab ?? activeTab.value,
          summary: detail.value?.summary ?? [],
          memberFields: detail.value?.memberFields ?? [],
          customFields: detail.value?.customFields ?? [],
          statusLines: detail.value?.statusLines ?? [],
          actions: detail.value?.actions ?? []
        }
      } catch {
        // 不阻断动作拉取主流程
      }
    }
    actions.value = await reflectionApi.listMethods(rk, idNum)
  } catch (e) {
    actions.value = []
    ElMessage.warning(`加载动作列表失败：${(e as Error).message}`)
  } finally {
    actionsLoading.value = false
  }
}

async function onExecute(m: ReflectionMethod) {
  // 无 methodName = workbench 兜底 action，保留旧的"提示"语义，避免误导用户以为已执行。
  if (!m.methodName) {
    ElMessage.info(`动作「${m.label || m.methodName}」需在 SimpleLite 工作台执行（当前对象类型未接入反射 execute）。`)
    return
  }
  const rk = reflectionKind.value
  if (!rk || !props.selection) return
  const idNum = Number(props.selection.id)
  if (!Number.isFinite(idNum)) {
    ElMessage.warning('选中对象 id 非数字，无法 execute')
    return
  }

  // 收集参数：单参数 / 多参数都串行 prompt。多参数 UI 比较糙但不需要新组件；后续若用得
  // 重再升级到 el-dialog 表单。中途取消任意一个 → 整体取消，不执行。
  const params: Record<string, string> = {}
  for (const p of m.params) {
    try {
      const promptDefault = p.hasDefault && p.defaultValue != null ? p.defaultValue : ''
      const { value } = await ElMessageBox.prompt(
        `参数：${p.name}（${p.typeName}）${p.hasDefault ? `，缺省 ${p.defaultValue ?? '""'}` : ''}`,
        `执行 ${m.label || m.methodName}`,
        {
          inputValue: promptDefault,
          confirmButtonText: '下一步',
          cancelButtonText: '取消',
          inputPlaceholder: m.hint ?? `请输入 ${p.name}`
        }
      )
      params[p.name] = value ?? ''
    } catch {
      // ElMessageBox 取消会抛 'cancel'；静默退出整个 execute 流程。
      return
    }
  }

  executing.value = m.methodName
  try {
    const r = await reflectionApi.execute(rk, idNum, m.methodName, params)
    ElMessage.success(
      r.returnValue ? `执行成功：${r.returnValue}` : `已执行 ${m.label || m.methodName}`
    )
  } catch (e) {
    ElMessage.error(`执行失败：${(e as Error).message}`)
  } finally {
    executing.value = null
  }
}

async function refreshForSelection() {
  activeTab.value = 'properties'
  await ensureOpsMonitorConfig()
  // detail + actions 并行拉，提速 ~RTT/2。actions 不影响 properties Tab 渲染；先回的先到位。
  await Promise.all([loadDetail(), loadActions()])
}

watch(() => props.selection, () => { void refreshForSelection() }, { deep: true })
watch(() => props.refreshKey, () => { void refreshForSelection() })
watch(activeTab, () => { void loadDetail() })

onMounted(() => { void refreshForSelection() })

function toDetailFields(rows: Array<ReflectionKv | { key: string; value: string }>): DetailField[] {
  return rows.map((r) => ({ key: r.key, value: r.value, locked: 'locked' in r ? r.locked : false }))
}
</script>

<style scoped>
.selection-detail { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.name { font-weight: 600; font-size: 13px; color: var(--mg-text-light); }
.detail-tabs :deep(.el-tabs__header) { margin-bottom: 0; }
.detail-tabs :deep(.el-tabs__item) { color: var(--mg-text-muted) !important; }
.detail-tabs :deep(.el-tabs__item.is-active) { color: var(--mg-text-light) !important; }
.detail-body { flex: 1; min-height: 120px; overflow: auto; color: var(--mg-text-light); }
.detail-body :deep(.el-descriptions__label) { color: var(--mg-text-muted) !important; }
.detail-body :deep(.el-descriptions__content),
.detail-body :deep(.el-table .cell) {
  color: var(--mg-text-light) !important;
}
.section { margin-top: 10px; }
.section-title { font-size: 12px; color: var(--mg-text-light); margin-bottom: 6px; }
.action-list { display: flex; flex-direction: column; gap: 6px; }
.action-btn {
  justify-content: flex-start;
  border-color: var(--mg-action-border);
  background: var(--mg-action-bg);
  color: #fff !important;
}
.action-btn:hover {
  border-color: var(--mg-action-border-hi);
  background: var(--mg-action-bg-hover);
}
.action-btn :deep(span) {
  color: #fff !important;
  font-weight: 600;
}
.action-btn .hint-icon { margin-left: 6px; font-size: 13px; opacity: 0.8; }
.empty { font-size: 12px; padding: 8px 0; color: var(--mg-text-light); }
.muted { color: var(--mg-text-muted); }
</style>
