<template>
  <PermissionGuard widget-id="ConfigCenter">
    <slot
      v-if="$slots.chrome"
      name="chrome"
      :payload="payload"
      :update="update"
      :save="save"
      :reload="reload"
      :saving="saving"
      :loading="loading"
      :envelope="envelope"
      :relative-time="relativeTime"
    />
    <el-card v-else shadow="never">
      <template #header>
        <div class="cpb-header">
          <span class="cpb-title">{{ title }}</span>
          <el-tag size="small" effect="plain">section={{ section }}</el-tag>
          <el-tag size="small" type="info" effect="plain">v{{ envelope?.version ?? '-' }}</el-tag>
          <el-tag size="small" type="success" effect="plain" v-if="envelope?.updatedAt">{{ relativeTime }}</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" @click="save()">保存</el-button>
        </div>
      </template>

      <p v-if="description" class="cpb-desc">{{ description }}</p>

      <div class="cpb-body">
        <el-row :gutter="12">
          <el-col :span="14">
            <el-card shadow="never" body-style="padding: 12px">
              <template #header>表单</template>
              <slot :payload="payload" :update="update" />
            </el-card>
          </el-col>
          <el-col :span="10">
            <el-card shadow="never" body-style="padding: 0">
              <template #header>JSON 预览（保存即下发占位）</template>
              <pre class="cpb-json">{{ jsonText }}</pre>
            </el-card>
          </el-col>
        </el-row>
      </div>
    </el-card>
  </PermissionGuard>
</template>

<script setup lang="ts" generic="T extends object">
import { computed, onMounted, ref, watch } from 'vue'
import { Refresh, Check } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import PermissionGuard from '@/components/PermissionGuard.vue'
import { useConfigStore } from '@/stores/config'
import type { ConfigEnvelope, ConfigSection } from '@/types/config'

const props = defineProps<{
  section: ConfigSection
  title: string
  description?: string
  defaults: T
  /** 加载/保存前规范化 payload（如 ops 补全 monitor 字段） */
  normalizePayload?: (payload: T) => T
  /** 加载完成后二次合并（如从 Simple3 拉 monitor） */
  afterLoad?: (payload: T, update: (next: T) => void) => void | Promise<void>
  /** 写入 Platform 之前先执行（如先落盘 Simple3 monitor） */
  beforeSave?: (payload: T) => void | Promise<void>
  /** 保存成功后副作用（如再次同步 Simple3） */
  afterSave?: (payload: T) => void | Promise<void>
}>()

const store = useConfigStore()
const envelope = ref<ConfigEnvelope<T> | null>(null)
const payload = ref<T>(JSON.parse(JSON.stringify(props.defaults)) as T)
const loading = ref(false)
const saving = ref(false)

const jsonText = computed(() => JSON.stringify(payload.value, null, 2))
const relativeTime = computed(() => envelope.value?.updatedAt
  ? new Date(envelope.value.updatedAt).toLocaleString('zh-CN')
  : '—')

function update(next: T) { payload.value = next }

function applyPayload(raw: T | undefined | null) {
  let next = raw
    ? (JSON.parse(JSON.stringify(raw)) as T)
    : (JSON.parse(JSON.stringify(props.defaults)) as T)
  if (props.normalizePayload) next = props.normalizePayload(next)
  payload.value = next
}

async function reloadFromServer(force: boolean, toastOnSuccess: boolean) {
  loading.value = true
  try {
    const env = await store.load<T>(props.section, force)
    envelope.value = env
    applyPayload(env.payload as T)
    if (props.afterLoad) await props.afterLoad(payload.value, update)
    if (toastOnSuccess) ElMessage.success('已重载')
  } catch (e) {
    ElMessage.error(`加载失败：${e instanceof Error ? e.message : String(e)}`)
  } finally {
    loading.value = false
  }
}

async function reload(force = false) {
  await reloadFromServer(force, force)
}

async function save(opts?: { toast?: boolean }): Promise<boolean> {
  const showToast = typeof opts === 'object' && opts && 'toast' in opts ? opts.toast !== false : true
  saving.value = true
  let body = payload.value
  if (props.normalizePayload) body = props.normalizePayload(body)

  if (props.beforeSave) {
    try {
      await props.beforeSave(body)
    } catch (e) {
      ElMessage.error(`地图监控配置保存失败：${e instanceof Error ? e.message : String(e)}`)
      saving.value = false
      return false
    }
  }

  let monitorSyncWarn = ''
  try {
    const env = await store.save<T>(props.section, body)
    envelope.value = env
    payload.value = body
    if (props.afterSave) {
      try {
        await props.afterSave(body)
      } catch (e) {
        monitorSyncWarn = `（平台配置已保存，但 Simple3 同步失败：${e instanceof Error ? e.message : String(e)}）`
      }
    }
    await reloadFromServer(true, false)
    const extra = props.beforeSave || props.afterSave ? '，地图监控配置已写入 Simple3' : ''
    if (showToast) ElMessage.success(`已保存 v${env.version}${extra}${monitorSyncWarn}`)
    return true
  } catch (e) {
    ElMessage.error(`保存失败：${e instanceof Error ? e.message : String(e)}`)
    return false
  } finally {
    saving.value = false
  }
}

watch(() => props.section, () => reload())

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

<style scoped>
.cpb-header { display: flex; align-items: center; gap: 8px; }
.cpb-title { font-weight: 600; font-size: 15px; color: var(--mg-text-light); }
.cpb-header .spacer { flex: 1; }
.cpb-desc {
  color: var(--mg-text-muted);
  font-size: 12.5px;
  margin: 0 0 14px;
  line-height: 1.6;
}
.cpb-body { margin-top: 4px; }
.cpb-json {
  margin: 0; padding: 14px;
  font-size: 12px; line-height: 1.55;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  background: var(--mg-veil-2);
  color: var(--mg-text-light);
  max-height: 520px;
  overflow: auto;
}
</style>
