<template>
  <ConfigPageBase
    section="ops"
    title="运营维护"
    description="调度回放、日志管理、版本维护（OpsConfig）"
    :defaults="DEFAULT_OPS"
    :normalize-payload="normalizeOpsConfig"
    :after-load="onOpsConfigLoaded"
    :before-save="onBeforePlatformSave">
    <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>
        <div v-loading="monitorSectionLoading" class="monitor-section">
          <el-alert
            type="info"
            :closable="false"
            show-icon
            :title="autoSaving ? '正在自动保存…' : '支持直接多选，修改后会自动保存到地图监控（simple.json），无需点击上方「保存」。若某组不勾选，表示该组在地图监控中显示全部。'"
            style="margin-bottom: 10px" />
          <el-form-item label="按车型动作配置" v-loading="carTypesLoading">
            <el-collapse v-model="expandedCarTypes" class="type-actions">
              <el-collapse-item
                v-for="row in carTypeActions"
                :key="row.typeKey"
                :name="row.typeKey"
                :title="`${row.title}（${row.methods.length}）`">
                <div v-show="expandedCarTypes.includes(row.typeKey)">
                  <el-checkbox-group
                    :key="carCheckboxGroupKey(payload, row)"
                    :model-value="carActionsForRow(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>
                </div>
              </el-collapse-item>
            </el-collapse>
            <p v-if="!monitorSectionLoading && !carTypesLoading && !carTypeActions.length" class="muted empty-hint">
              暂未扫描到带 MethodMember 的车型（请确认 SimpleLite 已连接并已加载插件）。
            </p>
          </el-form-item>
          <el-form-item label="站点动作显示项">
            <el-checkbox-group
              :model-value="payload.monitor?.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="payload.monitor?.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>
        </div>
      </el-form>
    </template>
  </ConfigPageBase>
</template>

<script setup lang="ts">
import { onBeforeUnmount, shallowRef, ref } from 'vue'
import { ElMessage } from 'element-plus'
import ConfigPageBase from '@/components/ConfigPageBase.vue'
import { DEFAULT_OPS } from '@/mock/data/configs'
import { reflectionApi } from '@/api/reflection'
import type { MonitorPanelPolicy, OpsConfig } from '@/types/config'
import { lookupCarActionKeys } from '@/utils/carActionByTypeLookup'
import { fetchMonitorConfigCached } from '@/utils/monitorConfigCache'
import {
  cloneCarActionByType,
  defaultMonitorPolicy,
  loadMonitorSettingsIntoOps,
  normalizeOpsConfig,
  saveMonitorSettingsFromOps,
  type MonitorConfigRoot
} 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 CAR_METHODS_CACHE_KEY = 'simple-platform-methods-by-type-car-v1'

const carTypeActions = shallowRef<CarTypeActionRow[]>([])
const siteMethods = shallowRef<MethodOption[]>([])
const trackMethods = shallowRef<MethodOption[]>([])
const monitorSectionLoading = ref(false)
const carTypesLoading = ref(false)
const expandedCarTypes = ref<string[]>([])

const cachedMonitorRoot = ref<MonitorConfigRoot | null>(null)

const autoSaving = ref(false)
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null
let pendingMonitorPayload: OpsConfig | null = null
const AUTO_SAVE_DELAY_MS = 500

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 {
    const saved = await saveMonitorSettingsFromOps(target, {
      cachedRoot: cachedMonitorRoot.value
    })
    cachedMonitorRoot.value = saved
    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()
})

async function onOpsConfigLoaded(payload: OpsConfig, update: (next: OpsConfig) => void) {
  monitorSectionLoading.value = true
  carTypesLoading.value = true
  try {
    const mc = await fetchMonitorConfigCached()
    const [{ config, cache }, _] = await Promise.all([
      loadMonitorSettingsIntoOps(payload, mc),
      loadCarTypeActions()
    ])
    cachedMonitorRoot.value = cache
    siteMethods.value = methodOptionsFromKeys(mc.available.site.methods)
    trackMethods.value = methodOptionsFromKeys(mc.available.track.methods)
    update(config)
  } catch {
    update(normalizeOpsConfig(payload))
    siteMethods.value = []
    trackMethods.value = []
    await loadCarTypeActions()
  } finally {
    monitorSectionLoading.value = false
    carTypesLoading.value = false
  }
}

async function onBeforePlatformSave(payload: OpsConfig) {
  cachedMonitorRoot.value = await saveMonitorSettingsFromOps(payload, {
    cachedRoot: cachedMonitorRoot.value
  })
}

async function loadCarTypeActions() {
  try {
    const raw = sessionStorage.getItem(CAR_METHODS_CACHE_KEY)
    if (raw) {
      const parsed = JSON.parse(raw) as CarTypeActionRow[]
      if (Array.isArray(parsed) && parsed.length) {
        carTypeActions.value = parsed
        return
      }
    }
  } catch { /* ignore */ }

  try {
    const rows = await reflectionApi.listMethodsByType('car')
    const mapped = 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))
    carTypeActions.value = mapped
    try {
      sessionStorage.setItem(CAR_METHODS_CACHE_KEY, JSON.stringify(mapped))
    } catch { /* quota */ }
  } catch {
    carTypeActions.value = []
  }
}

function methodOptionsFromKeys(keys?: string[] | null): MethodOption[] {
  if (!keys?.length) return []
  return keys.map((methodName) => ({ methodName, label: methodName }))
}

function ensureMonitor(payload: OpsConfig) {
  return payload.monitor ?? defaultMonitorPolicy()
}

function carActionsForRow(payload: OpsConfig, row: CarTypeActionRow): string[] {
  const map = ensureMonitor(payload).carActionByType
  return lookupCarActionKeys(map, row.typeKey, row.shortTypeName)
}

/** 展开/已选变化时强制 checkbox-group 重挂载，修复 Element Plus 勾选与 model 不同步。 */
function carCheckboxGroupKey(payload: OpsConfig, row: CarTypeActionRow): string {
  const selected = carActionsForRow(payload, row).slice().sort().join('\0')
  return `${row.typeKey}\0${selected}`
}

function updateMonitorList(
  update: (next: OpsConfig) => void,
  payload: OpsConfig,
  kind: MonitorKind,
  key: MonitorKey,
  list: string[]
) {
  const monitor = ensureMonitor(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 = ensureMonitor(payload)
  const nextMap = cloneCarActionByType(monitor.carActionByType)
  nextMap[row.typeKey] = [...methods]
  if (row.shortTypeName !== row.typeKey) {
    delete nextMap[row.shortTypeName]
  }
  const next: OpsConfig = {
    ...payload,
    monitor: {
      ...monitor,
      carActionByType: nextMap
    }
  }
  update(next)
  scheduleMonitorAutoSave(next)
}
</script>

<style scoped>
.monitor-section {
  min-height: 80px;
}
.cfg-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 4px 10px;
}
.type-actions {
  width: 100%;
}
.empty-hint {
  margin: 4px 0 0;
  font-size: 12px;
}
.muted {
  color: var(--mg-text-muted);
}
</style>
