<template>
  <PermissionGuard widget-id="ConfigCenter">
    <el-card shadow="never" class="mmc-card">
      <template #header>
        <div class="mmc-header">
          <span class="mmc-title">地图监控配置</span>
          <el-tag size="small" effect="plain">section=map-monitor</el-tag>
          <el-tag v-if="dirty" size="small" type="warning" effect="dark">未保存</el-tag>
          <div class="spacer" />
          <el-button size="small" :icon="Refresh" :loading="loading" @click="reload(true)">重载</el-button>
          <el-button size="small" type="primary" :icon="Check" :loading="saving" :disabled="!dirty" @click="save">
            保存
          </el-button>
        </div>
      </template>

      <p class="mmc-desc">
        勾选「地图监控」右侧选中信息面板要展示的内容白名单（按对象类型 - 车辆 / 站点 / 路径 分组）。
        三组分别控制 <b>属性</b>、<b>状态</b>、<b>动作</b> Tab 里可见的字段 / 状态项 / 可执行方法。
        每组留空 = 显示全部；勾选任何一项即进入白名单模式（仅显示已勾选）。保存后直接写入 SimpleLite 的
        <code>simple.json</code> （<code>Configuration.conf.monitorVisibility</code>）。
      </p>

      <el-tabs v-model="activeKind" class="mmc-tabs">
        <el-tab-pane v-for="k in KINDS" :key="k.value" :label="k.label" :name="k.value">
          <div v-loading="loading" class="mmc-tab-body">
            <MapMonitorConfigGroup
              title="属性字段（Properties）"
              :available="available[k.value].fields"
              :selected="config[k.value].fields"
              empty-tip="该类型暂未发现任何 [FieldMember]。在 SimpleLite 端添加字段后点重载会刷新。"
              @update:selected="(v: string[]) => onUpdate(k.value, 'fields', v)" />

            <MapMonitorConfigGroup
              title="状态项（Status）"
              :available="available[k.value].status"
              :selected="config[k.value].status"
              empty-tip="该类型暂未发现任何 status 字段（来自 obj.status 反射）。"
              @update:selected="(v: string[]) => onUpdate(k.value, 'status', v)" />

            <MapMonitorConfigGroup
              title="可执行动作（Methods）"
              :available="available[k.value].methods"
              :selected="config[k.value].methods"
              empty-tip="该类型暂未发现任何 [MethodMember] 方法。"
              @update:selected="(v: string[]) => onUpdate(k.value, 'methods', v)" />
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-card>
  </PermissionGuard>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { Refresh, Check } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import PermissionGuard from '@/components/PermissionGuard.vue'
import MapMonitorConfigGroup from '@/components/config/MapMonitorConfigGroup.vue'
import {
  reflectionApi,
  emptyMonitorVisibilityMap,
  emptyMonitorAvailableMap,
  type MonitorVisibilityKind,
  type MonitorVisibilityMap
} from '@/api/reflection'

// 三种 kind 在这一页是写死的：地图监控当前只暴露车辆 / 站点 / 路径（Configuration.conf.monitorVisibility
// 也是这三组）。新增 kind（比如 special / image）需要同步：后端 MonitorVisibilityConfig 加字段、
// ReflectionApiController.BuildAvailableForKind 加 case、这里加一行。
const KINDS: ReadonlyArray<{ value: MonitorVisibilityKind; label: string }> = [
  { value: 'car', label: '车辆 (Car)' },
  { value: 'site', label: '站点 (Site)' },
  { value: 'track', label: '路径 (Track)' }
]

const activeKind = ref<MonitorVisibilityKind>('car')
const loading = ref(false)
const saving = ref(false)

// reactive 而非 ref：双层嵌套结构 + 内部数组要被子组件直接 set，用 reactive 让 Vue 能跟踪到
// `config.car.fields = [...]` 这类替换写法。每次重载用 Object.assign 替换字段，而不是替换整棵
// 树，避免和子组件已经绑定的引用脱钩。
const config = reactive<MonitorVisibilityMap>(emptyMonitorVisibilityMap())
const available = reactive<MonitorVisibilityMap>(emptyMonitorAvailableMap())

// 上一次保存（或最近一次 reload）的 config 快照，用来判断 dirty。stringify 比较够用——
// 字段全是 string[]，stringify 在百级规模性能不是瓶颈。
const baseline = ref<string>(JSON.stringify(emptyMonitorVisibilityMap()))

const dirty = computed(() => baseline.value !== JSON.stringify(config))

function applyMap(target: MonitorVisibilityMap, src: MonitorVisibilityMap) {
  for (const k of KINDS) {
    target[k.value].fields = [...(src[k.value]?.fields ?? [])]
    target[k.value].status = [...(src[k.value]?.status ?? [])]
    target[k.value].methods = [...(src[k.value]?.methods ?? [])]
  }
}

async function reload(force = false) {
  loading.value = true
  try {
    const payload = await reflectionApi.getMonitorConfig()
    applyMap(config, payload.config)
    applyMap(available, payload.available)
    baseline.value = JSON.stringify(config)
    if (force) ElMessage.success('已重载')
  } catch (e) {
    ElMessage.error(`加载失败：${e instanceof Error ? e.message : String(e)}`)
  } finally {
    loading.value = false
  }
}

async function save() {
  saving.value = true
  try {
    const saved = await reflectionApi.saveMonitorConfig({
      car: { ...config.car },
      site: { ...config.site },
      track: { ...config.track }
    })
    // 后端会清洗（去重 / 去空 / 截断超长 key），可能与前端发的不完全相等 —— 以服务端返回为准。
    applyMap(config, saved)
    baseline.value = JSON.stringify(config)
    ElMessage.success('已保存（已写入 simple.json）')
  } catch (e) {
    ElMessage.error(`保存失败：${e instanceof Error ? e.message : String(e)}`)
  } finally {
    saving.value = false
  }
}

function onUpdate(kind: MonitorVisibilityKind, group: 'fields' | 'status' | 'methods', next: string[]) {
  // 创建新数组而非 splice：让 Vue 的依赖追踪稳定捕获到 set；同时让 dirty 计算的 stringify
  // 拿到的是最新引用。
  config[kind][group] = [...next]
}

onMounted(() => reload())
</script>

<style scoped>
.mmc-card { margin: 0; }
.mmc-header { display: flex; align-items: center; gap: 8px; }
.mmc-title { font-weight: 600; font-size: 15px; color: var(--mg-text-light); }
.mmc-header .spacer { flex: 1; }
.mmc-desc {
  color: var(--mg-text-muted);
  font-size: 12.5px;
  margin: 0 0 12px;
  line-height: 1.7;
}
.mmc-desc code {
  background: var(--mg-veil-2);
  padding: 1px 5px;
  border-radius: 4px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
}
.mmc-tabs :deep(.el-tabs__item) { color: var(--mg-text-muted) !important; }
.mmc-tabs :deep(.el-tabs__item.is-active) { color: var(--mg-text-light) !important; }
.mmc-tab-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding-top: 6px;
}
</style>
