<template>
  <div class="cse-root" v-loading="loading">
    <!-- 报警调色板 ─────────────────────────────────────────────── -->
    <el-card shadow="never" class="cse-card">
      <template #header>
        <div class="cse-card-header">
          <span class="cse-card-title">报警 / 状态调色板</span>
          <el-tag size="small" type="info" effect="plain">7 种默认状态</el-tag>
          <span class="cse-card-spacer" />
          <el-button size="small" :loading="resettingPalette" @click="resetPaletteToDefault">恢复默认</el-button>
          <el-button size="small" type="primary" :disabled="!paletteDirty" :loading="savingPalette" @click="savePalette">
            保存调色板（写入 simple.json）
          </el-button>
        </div>
      </template>

      <p class="cse-desc">
        车体颜色的判定优先级：
        <el-tag size="small" effect="plain">1. AlarmInfo 非空 → 「报警」</el-tag>
        <el-tag size="small" effect="plain">2. lstatus 包含关键字 → 对应键</el-tag>
        <el-tag size="small" effect="plain">3. 兜底 → 上下线默认色</el-tag>
        修改下方颜色 + 「保存调色板」即时生效。
      </p>

      <div class="cse-palette">
        <div v-for="entry in paletteEntries" :key="entry.key" class="cse-palette-item">
          <div class="cse-palette-label">
            <span class="cse-palette-key">{{ entry.key }}</span>
            <el-tag v-if="!entry.isDefault" size="small" type="warning" effect="plain">自定义</el-tag>
          </div>
          <div class="cse-palette-pickers">
            <el-color-picker
              :model-value="argbToHex(getPaletteColor(entry.key))"
              show-alpha
              size="small"
              @update:model-value="(v: string | null) => setPaletteColor(entry.key, v)"
            />
            <code class="cse-color-code">#{{ argbToHex(getPaletteColor(entry.key)).toUpperCase() }}</code>
          </div>
        </div>
      </div>
    </el-card>

    <!-- 车型样式 ─────────────────────────────────────────────── -->
    <el-card shadow="never" class="cse-card cse-types-card">
      <template #header>
        <div class="cse-card-header">
          <span class="cse-card-title">车型样式</span>
          <el-tag size="small" type="info" effect="plain">{{ types.length }} 个已加载车型</el-tag>
          <span class="cse-card-spacer" />
          <el-button size="small" @click="refresh">
            <el-icon><Refresh /></el-icon>
            <span class="cse-btn-text">刷新</span>
          </el-button>
        </div>
      </template>

      <p class="cse-desc">
        点击车型行展开编辑：尺寸（米）、车体 / 描边 / 标签颜色、是否显示名称、3D 模型文件路径。
        每辆车按 .NET 类型 (`assemblyName::shortName`) 索引样式。修改后点行内「保存」即时写入并生效。
      </p>

      <el-collapse v-model="activeKey" accordion>
        <el-collapse-item v-for="row in types" :key="row.typeName" :name="row.typeName">
          <template #title>
            <div class="cse-row-title">
              <span class="cse-row-name">{{ row.label }}</span>
              <code class="cse-row-fqn">{{ row.typeName }}</code>
              <el-tag v-if="row.hasOverride" size="small" type="success" effect="plain">已覆盖</el-tag>
              <el-tag v-else size="small" type="info" effect="plain">默认</el-tag>
            </div>
          </template>

          <CarStyleEditorForm
            v-if="forms[row.typeName]"
            :type-full-name="row.typeName"
            :model-value="forms[row.typeName]!"
            :has-override="row.hasOverride"
            :saving="savingTypeName === row.typeName"
            @update:model-value="(v: CarStyleDto) => forms[row.typeName] = v"
            @save="onSaveType(row)"
            @reset="onResetType(row)"
          />
        </el-collapse-item>
      </el-collapse>

      <el-empty v-if="!loading && types.length === 0" description="未发现任何 Car 子类（请确认 SimpleLite 已加载相应插件 dll）" />
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import CarStyleEditorForm from './CarStyleEditorForm.vue'
import {
  reflectionApi,
  type AlarmColorEntry,
  type CarStyleDto,
  type CarStyleTypeRow
} from '@/api/reflection'

const loading = ref(false)
const savingPalette = ref(false)
const resettingPalette = ref(false)
const savingTypeName = ref<string | null>(null)

const types = ref<CarStyleTypeRow[]>([])
const forms = reactive<Record<string, CarStyleDto>>({})
const activeKey = ref<string | undefined>(undefined)

const defaultKeys = ref<string[]>([])
const paletteEntries = ref<AlarmColorEntry[]>([])
/** 当前正在编辑、尚未持久化的颜色映射 */
const paletteDraft = reactive<Record<string, number>>({})
/** refresh 时记录服务器返回的快照，用于 diff 判定 dirty */
const paletteSnapshot = reactive<Record<string, number>>({})

const paletteDirty = computed(() => {
  const keys = new Set([...Object.keys(paletteDraft), ...Object.keys(paletteSnapshot)])
  for (const k of keys) if (paletteDraft[k] !== paletteSnapshot[k]) return true
  return false
})

function getPaletteColor(key: string): number {
  return paletteDraft[key] ?? 0
}
function setPaletteColor(key: string, hex: string | null) {
  paletteDraft[key] = hex ? hexToArgb(hex) : 0
}

/** Element Plus el-color-picker 用 `#RRGGBBAA`（show-alpha）。 */
function argbToHex(argb: number): string {
  const a = (argb >>> 24) & 0xff
  const r = (argb >>> 16) & 0xff
  const g = (argb >>> 8) & 0xff
  const b = argb & 0xff
  const hex = (n: number) => n.toString(16).padStart(2, '0')
  return `${hex(r)}${hex(g)}${hex(b)}${hex(a)}`
}
function hexToArgb(hex: string): number {
  const m = hex.replace(/^#/, '')
  if (m.length === 6) {
    const r = parseInt(m.slice(0, 2), 16)
    const g = parseInt(m.slice(2, 4), 16)
    const b = parseInt(m.slice(4, 6), 16)
    return ((0xff << 24) | (r << 16) | (g << 8) | b) >>> 0
  }
  if (m.length === 8) {
    const r = parseInt(m.slice(0, 2), 16)
    const g = parseInt(m.slice(2, 4), 16)
    const b = parseInt(m.slice(4, 6), 16)
    const a = parseInt(m.slice(6, 8), 16)
    return ((a << 24) | (r << 16) | (g << 8) | b) >>> 0
  }
  return 0
}

async function refresh() {
  loading.value = true
  try {
    // 会话 N+2：car-types 与 alarm-colors 独立 await，避免一边失败把另一边的成功也吞掉。
    // 任意一边 SimpleLite 未启动 / 返回了非预期结构（YARP 502 / null payload），都不要让整个面板白屏崩。
    const carTypesResp = await reflectionApi.getCarStyleTypes().catch((err) => {
      console.warn('[CarStyleEditor] getCarStyleTypes failed:', err)
      return null
    })
    const paletteResp = await reflectionApi.getAlarmColors().catch((err) => {
      console.warn('[CarStyleEditor] getAlarmColors failed:', err)
      return null
    })

    // 防御 1：car-types 可能为 null / 缺 types 字段 / types 非数组。
    const carTypesList = Array.isArray(carTypesResp?.types) ? carTypesResp!.types : []
    types.value = carTypesList
    for (const row of carTypesList) {
      forms[row.typeName] = { ...row.style }
    }
    for (const k of Object.keys(forms)) {
      if (!carTypesList.find((r) => r.typeName === k)) delete forms[k]
    }

    // 防御 2：palette 可能为 null / 缺 entries 字段 / entries 非数组（用户截图正是此处崩）。
    const safeEntries = Array.isArray(paletteResp?.entries) ? paletteResp!.entries : []
    const safeDefaultKeys = Array.isArray(paletteResp?.defaultKeys) ? paletteResp!.defaultKeys : []
    defaultKeys.value = safeDefaultKeys
    paletteEntries.value = safeEntries
    for (const k of Object.keys(paletteDraft)) delete paletteDraft[k]
    for (const k of Object.keys(paletteSnapshot)) delete paletteSnapshot[k]
    for (const e of safeEntries) {
      if (!e?.key) continue
      const argb = Number(e.colorArgb) >>> 0
      paletteDraft[e.key] = argb
      paletteSnapshot[e.key] = argb
    }

    if (!carTypesResp || !paletteResp) {
      ElMessage.warning('车型样式 / 报警颜色加载部分失败，请确认 SimpleLite 已启动（端口 8222）。')
    }
  } catch (err) {
    ElMessage.error(`加载车型样式失败：${(err as Error).message}`)
  } finally {
    loading.value = false
  }
}

async function savePalette() {
  if (!paletteDirty.value) return
  savingPalette.value = true
  try {
    await reflectionApi.putAlarmColors({ ...paletteDraft })
    ElMessage.success('调色板已写入并生效')
    await refresh()
  } catch (err) {
    ElMessage.error(`保存调色板失败：${(err as Error).message}`)
  } finally {
    savingPalette.value = false
  }
}

function resetPaletteToDefault() {
  resettingPalette.value = true
  const defaultArgb: Record<string, number> = {
    避障: 0xffff9800,
    运行: 0xff4caf50,
    上线: 0xff2196f3,
    下线: 0xff9e9e9e,
    导航失联: 0xff9c27b0,
    报警: 0xfff44336,
    低电量: 0xffffc107
  }
  for (const k of Object.keys(paletteDraft)) delete paletteDraft[k]
  for (const k of Object.keys(defaultArgb)) paletteDraft[k] = defaultArgb[k]
  resettingPalette.value = false
  ElMessage.info('已恢复为 7 种默认颜色，未写入。点「保存调色板」生效。')
}

async function onSaveType(row: CarStyleTypeRow) {
  const body = forms[row.typeName]
  if (!body) return
  savingTypeName.value = row.typeName
  try {
    const saved = await reflectionApi.putCarStyle(row.typeName, body)
    forms[row.typeName] = { ...saved }
    row.hasOverride = true
    ElMessage.success(`已保存 ${row.label} 车型样式`)
  } catch (err) {
    ElMessage.error(`保存 ${row.label} 失败：${(err as Error).message}`)
  } finally {
    savingTypeName.value = null
  }
}

async function onResetType(row: CarStyleTypeRow) {
  try {
    await ElMessageBox.confirm(
      `把 ${row.label} (${row.typeName}) 的样式恢复为全局默认？删除条目后将与未覆盖车型一致。`,
      '恢复默认',
      { confirmButtonText: '恢复', cancelButtonText: '取消', type: 'warning' }
    )
  } catch {
    return
  }
  try {
    await reflectionApi.deleteCarStyle(row.typeName)
    ElMessage.success(`已恢复 ${row.label} 为默认样式`)
    await refresh()
  } catch (err) {
    ElMessage.error(`恢复默认失败：${(err as Error).message}`)
  }
}

onMounted(refresh)
defineExpose({ refresh })
</script>

<style scoped>
.cse-root {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
  box-sizing: border-box;
  overflow: auto;
}
.cse-card :deep(.el-card__body) {
  padding: 12px 16px;
}
.cse-card-header {
  display: flex;
  align-items: center;
  gap: 10px;
}
.cse-card-title {
  font-weight: 600;
}
.cse-card-spacer {
  flex: 1;
}
.cse-desc {
  margin: 0 0 12px 0;
  color: var(--el-text-color-primary);
  font-size: 13px;
  line-height: 1.65;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.cse-palette {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
}
.cse-palette-item {
  border: 1px solid var(--el-border-color);
  background: var(--el-fill-color-light);
  border-radius: 6px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.cse-palette-label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.cse-palette-key {
  font-weight: 600;
  color: var(--el-text-color-primary);
}
.cse-palette-pickers {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cse-color-code {
  font-family: 'JetBrains Mono', Consolas, monospace;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.cse-types-card {
  flex: 1;
}
.cse-row-title {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.cse-row-name {
  font-weight: 600;
}
.cse-row-fqn {
  font-family: 'JetBrains Mono', Consolas, monospace;
  font-size: 12px;
  color: var(--el-text-color-secondary);
  flex: 1;
  text-align: left;
  word-break: break-all;
}
.cse-btn-text {
  margin-left: 4px;
}
</style>
