<template>
  <ConfigPageBase
    section="ops"
    title="运营维护"
    description="调度回放、日志管理、版本维护（OpsConfig）"
    :defaults="DEFAULT_OPS"
    :normalize-payload="normalizeOpsConfig"
    :after-load="onOpsConfigLoaded"
    :before-save="saveMonitorSettingsFromOps">
    <template #default="{ payload, update }">
      <el-form label-width="200px" :model="payload">
        <el-divider content-position="left">调度回放</el-divider>
        <el-form-item label="保留时长 (天)">
          <el-input-number :model-value="payload.playback.retentionDays" :min="1" :max="3650" @update:model-value="(v: number | undefined) => update({ ...payload, playback: { ...payload.playback, retentionDays: v ?? 0 } })" />
        </el-form-item>
        <el-form-item label="采样频率 (Hz)">
          <el-input-number :model-value="payload.playback.samplingHz" :min="1" :max="100" @update:model-value="(v: number | undefined) => update({ ...payload, playback: { ...payload.playback, samplingHz: v ?? 0 } })" />
        </el-form-item>
        <el-divider content-position="left">日志保留</el-divider>
        <el-form-item label="热数据 (天)">
          <el-input-number :model-value="payload.logRetention.hotDays" :min="1" :max="365" @update:model-value="(v: number | undefined) => update({ ...payload, logRetention: { ...payload.logRetention, hotDays: v ?? 0 } })" />
        </el-form-item>
        <el-form-item label="冷数据 (天)">
          <el-input-number :model-value="payload.logRetention.coldDays" :min="1" :max="3650" @update:model-value="(v: number | undefined) => update({ ...payload, logRetention: { ...payload.logRetention, coldDays: v ?? 0 } })" />
        </el-form-item>
        <el-divider content-position="left">版本维护</el-divider>
        <el-form-item label="保留发布版本">
          <el-input-number :model-value="payload.version.keepReleases" :min="1" :max="100" @update:model-value="(v: number | undefined) => update({ ...payload, version: { keepReleases: v ?? 0 } })" />
        </el-form-item>
        <el-divider content-position="left">地图监控信息与动作管理</el-divider>
        <el-alert
          type="info"
          :closable="false"
          show-icon
          :title="autoSaving ? '正在自动保存…' : '支持直接多选，修改后会自动保存到地图监控（simple.json），无需点击上方「保存」。若某组不勾选，表示该组在地图监控中显示全部。'"
          style="margin-bottom: 10px" />
        <el-form-item label="按车型动作配置">
          <el-collapse class="type-actions">
            <el-collapse-item
              v-for="row in carTypeActions"
              :key="row.typeKey"
              :name="row.typeKey"
              :title="`${row.title}（${row.methods.length}）`">
              <el-checkbox-group
                :model-value="getCarTypeActions(payload, row)"
                class="cfg-check-grid"
                @update:model-value="(v) => updateCarTypeActions(update, payload, row, v as string[])">
                <el-checkbox v-for="m in row.methods" :key="`${row.typeKey}-${m.methodName}`" :label="m.methodName">
                  {{ m.label }}
                </el-checkbox>
              </el-checkbox-group>
            </el-collapse-item>
          </el-collapse>
        </el-form-item>
        <el-form-item label="站点动作显示项">
          <el-checkbox-group
            :model-value="getMonitor(payload).site.actionKeys"
            class="cfg-check-grid"
            @update:model-value="(v) => updateMonitorList(update, payload, 'site', 'actionKeys', v as string[])">
            <el-checkbox v-for="m in siteMethods" :key="`sa-${m.methodName}`" :label="m.methodName">
              {{ m.label }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="路径动作显示项">
          <el-checkbox-group
            :model-value="getMonitor(payload).track.actionKeys"
            class="cfg-check-grid"
            @update:model-value="(v) => updateMonitorList(update, payload, 'track', 'actionKeys', v as string[])">
            <el-checkbox v-for="m in trackMethods" :key="`ta-${m.methodName}`" :label="m.methodName">
              {{ m.label }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
      </el-form>
    </template>
  </ConfigPageBase>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import ConfigPageBase from '@/components/ConfigPageBase.vue'
import { DEFAULT_OPS } from '@/mock/data/configs'
import { reflectionApi, emptyMonitorAvailableMap, type MonitorVisibilityMap } from '@/api/reflection'
import type { MonitorOpsPolicy, MonitorPanelPolicy, OpsConfig } from '@/types/config'
import {
  loadMonitorSettingsIntoOps,
  normalizeOpsConfig,
  saveMonitorSettingsFromOps
} from '@/utils/opsMonitorConfig'

type MonitorKind = 'car' | 'site' | 'track'
type MonitorKey = keyof MonitorPanelPolicy

interface MethodOption {
  methodName: string
  label: string
}
interface CarTypeActionRow {
  typeKey: string
  shortTypeName: string
  title: string
  methods: MethodOption[]
}

const available = reactive<MonitorVisibilityMap>(emptyMonitorAvailableMap())
const carTypeActions = ref<CarTypeActionRow[]>([])
const siteMethods = ref<MethodOption[]>([])
const trackMethods = ref<MethodOption[]>([])

// 地图监控信息与动作管理这三组多选写入 SimpleLite（simple.json），与上方「保存」按钮走的
// Platform ops 配置是两条独立链路。这里做勾选即自动保存：防抖合并连续勾选，串行避免并发
// getMonitorConfig/saveMonitorConfig 竞态，卸载时把窗口内未落盘的改动补存。
const autoSaving = ref(false)
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null
let pendingMonitorPayload: OpsConfig | null = null
const AUTO_SAVE_DELAY_MS = 450

function scheduleMonitorAutoSave(next: OpsConfig) {
  pendingMonitorPayload = next
  if (autoSaveTimer) clearTimeout(autoSaveTimer)
  autoSaveTimer = setTimeout(() => { void flushMonitorAutoSave() }, AUTO_SAVE_DELAY_MS)
}

async function flushMonitorAutoSave() {
  autoSaveTimer = null
  if (autoSaving.value) {
    // 上一次保存仍在进行：稍后重试，确保最新勾选最终落盘。
    autoSaveTimer = setTimeout(() => { void flushMonitorAutoSave() }, 200)
    return
  }
  const target = pendingMonitorPayload
  pendingMonitorPayload = null
  if (!target) return
  autoSaving.value = true
  try {
    await saveMonitorSettingsFromOps(target)
    ElMessage.success({ message: '地图监控配置已自动保存', duration: 1200, grouping: true })
  } catch (e) {
    ElMessage.error(`地图监控配置自动保存失败：${e instanceof Error ? e.message : String(e)}`)
  } finally {
    autoSaving.value = false
    if (pendingMonitorPayload) scheduleMonitorAutoSave(pendingMonitorPayload)
  }
}

onBeforeUnmount(() => {
  if (autoSaveTimer) {
    clearTimeout(autoSaveTimer)
    autoSaveTimer = null
  }
  if (pendingMonitorPayload && !autoSaving.value) void flushMonitorAutoSave()
})

function normalizeMonitor(monitor?: MonitorOpsPolicy): MonitorOpsPolicy {
  return normalizeOpsConfig({ monitor } as OpsConfig).monitor
}

async function onOpsConfigLoaded(payload: OpsConfig, update: (next: OpsConfig) => void) {
  update(await loadMonitorSettingsIntoOps(payload))
}

function getMonitor(payload: OpsConfig): MonitorOpsPolicy {
  return normalizeMonitor(payload.monitor)
}

function updateMonitorList(
  update: (next: OpsConfig) => void,
  payload: OpsConfig,
  kind: MonitorKind,
  key: MonitorKey,
  list: string[]
) {
  const monitor = getMonitor(payload)
  const next: OpsConfig = {
    ...payload,
    monitor: {
      ...monitor,
      [kind]: {
        ...monitor[kind],
        [key]: [...list]
      }
    }
  }
  update(next)
  scheduleMonitorAutoSave(next)
}

function updateCarTypeActions(
  update: (next: OpsConfig) => void,
  payload: OpsConfig,
  row: CarTypeActionRow,
  methods: string[]
) {
  const monitor = getMonitor(payload)
  const nextMap = {
    ...monitor.carActionByType,
    [row.typeKey]: [...methods]
  }
  if (row.shortTypeName !== row.typeKey) {
    delete nextMap[row.shortTypeName]
  }
  const next: OpsConfig = {
    ...payload,
    monitor: {
      ...monitor,
      carActionByType: nextMap
    }
  }
  update(next)
  scheduleMonitorAutoSave(next)
}

function getCarTypeActions(payload: OpsConfig, row: CarTypeActionRow): string[] {
  const monitor = getMonitor(payload)
  return monitor.carActionByType[row.typeKey]
    ?? monitor.carActionByType[row.shortTypeName]
    ?? []
}

onMounted(async () => {
  try {
    const mc = await reflectionApi.getMonitorConfig()
    available.car = mc.available.car
    available.site = mc.available.site
    available.track = mc.available.track
  } catch {
    available.car = { fields: [], status: [], methods: [] }
    available.site = { fields: [], status: [], methods: [] }
    available.track = { fields: [], status: [], methods: [] }
  }
  try {
    const rows = await reflectionApi.listMethodsByType('car')
    carTypeActions.value = rows
      .map((r) => ({
        typeKey: r.fullTypeName ?? r.typeName,
        shortTypeName: r.typeName,
        title: `${r.typeLabel ?? r.typeName} / ${r.fullTypeName ?? r.typeName} [${r.assemblyName}]`,
        methods: r.methods
          .map((m) => ({ methodName: m.methodName, label: m.label || m.methodName }))
          .filter((m) => !!m.methodName)
      }))
      .filter((r) => r.methods.length > 0)
      .sort((a, b) => a.title.localeCompare(b.title))
  } catch {
    carTypeActions.value = []
  }
  try {
    const rows = await reflectionApi.listMethodsByType('site')
    siteMethods.value = dedupeMethodOptions(rows.flatMap((r) => r.methods))
  } catch {
    siteMethods.value = available.site.methods.map((m) => ({ methodName: m, label: m }))
  }
  try {
    const rows = await reflectionApi.listMethodsByType('track')
    trackMethods.value = dedupeMethodOptions(rows.flatMap((r) => r.methods))
  } catch {
    trackMethods.value = available.track.methods.map((m) => ({ methodName: m, label: m }))
  }
})

function dedupeMethodOptions(methods: Array<{ methodName: string; label?: string | null }>): MethodOption[] {
  const map = new Map<string, MethodOption>()
  for (const m of methods) {
    if (!m.methodName) continue
    if (!map.has(m.methodName)) {
      map.set(m.methodName, { methodName: m.methodName, label: m.label || m.methodName })
    }
  }
  return [...map.values()].sort((a, b) => a.label.localeCompare(b.label))
}
</script>

<style scoped>
.cfg-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 10px;
}
.type-actions {
  width: 100%;
}
</style>
