<template>
  <div class="ems-dashboard-editor-container" style="background-color:#ffffff">
    <div class="header-row">
      <div class="title-block">
        <div class="page-title">单站监控项目点位配置</div>
        <div class="page-desc">
          <span class="site-label">站点：</span>
          <span>{{ siteName || siteId || '-' }}</span>
        </div>
      </div>
    </div>

    <el-alert
      title="当前字段清单与点位映射均从后端表读取，保存后可直接用于单站监控查询。"
      type="info"
      :closable="false"
      style="margin-top: 16px;"
    />

    <el-tabs v-model="activeConfigTab" style="margin-top: 16px;">
      <el-tab-pane label="点位配置" name="point">
        <div class="mode-switch-row">
          <el-radio-group v-model="configMode" size="small">
            <el-radio-button label="table">列表配置</el-radio-button>
            <el-radio-button label="quick">快速配置</el-radio-button>
            <el-radio-button label="section">分组填报</el-radio-button>
          </el-radio-group>
          <div class="save-status-text">{{ saveStatusText }}</div>
          <el-input
            v-model.trim="quickFilter"
            size="small"
            clearable
            class="quick-filter-input"
            placeholder="按字段名/展示名/设备名筛选"
          />
          <div class="filter-actions" :class="{ 'single-battery-actions-visible': showSingleBatteryImportActions }">
            <el-button size="small" :disabled="!siteId" @click="openImportConfigDialog">导入配置</el-button>
            <el-button size="small" type="primary" :disabled="!siteId" @click="exportConfig">导出配置</el-button>
            <el-button v-if="showSingleBatteryImportActions" size="small" type="success" :disabled="!siteId || singleBatteryInitLoading" @click="openSingleBatteryInitDialog">
              {{ singleBatteryInitLoading ? '初始化中...' : '初始化单体电池配置' }}
            </el-button>
            <input
              ref="configInput"
              type="file"
              accept=".csv"
              style="display: none"
              @change="handleConfigFileChange"
            />
          </div>
        </div>

        <el-tabs v-model="activeTopMenu" style="margin-top: 16px;">
      <el-tab-pane
        v-for="topMenu in topMenuList"
        :key="topMenu.code"
        :label="topMenu.name"
        :name="topMenu.code"
      >
        <template v-if="configMode === 'section'">
          <div v-if="!topMenu.children || topMenu.children.length === 0" class="section-wrapper">
            <el-collapse
              accordion
              class="section-collapse"
              :value="getSectionActiveValue(buildSectionPanelKey(topMenu.code, 'ROOT'), getFilteredSectionBlocks(topMenu.items))"
              @input="setSectionActiveValue(buildSectionPanelKey(topMenu.code, 'ROOT'), $event)"
            >
              <el-collapse-item
                v-for="sectionBlock in getFilteredSectionBlocks(topMenu.items)"
                :key="sectionBlock.key"
                :name="sectionBlock.key"
              >
                <template slot="title">
                  <div class="section-title-row">
                    <span>{{ sectionBlock.section }}</span>
                    <span class="section-badge">{{ sectionBlock.fields.length }} 个字段</span>
                  </div>
                </template>
                <div class="section-field-list">
                  <div v-for="group in sectionBlock.fields" :key="group.key" class="section-field-card">
                    <div class="quick-card-header">
                      <div class="quick-title-block">
                        <div class="quick-title">{{ group.name }}</div>
                        <div class="quick-sub-title">{{ group.field }}</div>
                      </div>
                      <el-button type="text" size="mini" @click="clearGroupMapping(group)">清空</el-button>
                    </div>
                      <div v-for="row in group.rows" :key="row.code" class="quick-row">
                        <div class="quick-device-name">{{ row.deviceName || '默认映射' }}</div>
                        <el-input
                          v-model.trim="row.point"
                          :readonly="!!row.deviceId"
                          :placeholder="row.deviceId ? '点击选择点位' : '请输入点位'"
                          clearable
                          class="quick-input"
                          @focus="handlePointInputFocus(row)"
                          @mousedown.native.prevent="handlePointInputFocus(row)"
                        />
                        <el-input v-model.trim="row.fixedValue" placeholder="固定展示值" clearable class="quick-input" />
                        <el-checkbox v-model="row.useFixedDisplay" :true-label="1" :false-label="0">固定展示</el-checkbox>
                      </div>
                  </div>
                </div>
              </el-collapse-item>
            </el-collapse>
            <el-empty v-if="getFilteredSectionBlocks(topMenu.items).length === 0" description="暂无可配置项" />
          </div>

          <el-tabs
            v-else
            v-model="activeChildMap[topMenu.code]"
            class="child-menu-tabs"
            type="border-card"
          >
            <el-tab-pane
              v-for="child in topMenu.children"
              :key="child.code"
              :label="child.name"
              :name="child.code"
            >
              <div class="section-wrapper">
                <el-collapse
                  accordion
                  class="section-collapse"
                  :value="getSectionActiveValue(buildSectionPanelKey(topMenu.code, child.code), getFilteredSectionBlocks(child.items))"
                  @input="setSectionActiveValue(buildSectionPanelKey(topMenu.code, child.code), $event)"
                >
                  <el-collapse-item
                    v-for="sectionBlock in getFilteredSectionBlocks(child.items)"
                    :key="sectionBlock.key"
                    :name="sectionBlock.key"
                  >
                    <template slot="title">
                      <div class="section-title-row">
                        <span>{{ sectionBlock.section }}</span>
                        <span class="section-badge">{{ sectionBlock.fields.length }} 个字段</span>
                      </div>
                    </template>
                    <div class="section-field-list">
                      <div v-for="group in sectionBlock.fields" :key="group.key" class="section-field-card">
                        <div class="quick-card-header">
                          <div class="quick-title-block">
                            <div class="quick-title">{{ group.name }}</div>
                            <div class="quick-sub-title">{{ group.field }}</div>
                          </div>
                          <el-button type="text" size="mini" @click="clearGroupMapping(group)">清空</el-button>
                        </div>
                    <div v-for="row in group.rows" :key="row.code" class="quick-row">
                      <div class="quick-device-name">{{ row.deviceName || '默认映射' }}</div>
                      <el-input
                        v-model.trim="row.point"
                        :readonly="!!row.deviceId"
                        :placeholder="row.deviceId ? '点击选择点位' : '请输入点位'"
                        clearable
                        class="quick-input"
                        @focus="handlePointInputFocus(row)"
                        @mousedown.native.prevent="handlePointInputFocus(row)"
                      />
                      <el-input v-model.trim="row.fixedValue" placeholder="固定展示值" clearable class="quick-input" />
                      <el-checkbox v-model="row.useFixedDisplay" :true-label="1" :false-label="0">固定展示</el-checkbox>
                    </div>
                      </div>
                    </div>
                  </el-collapse-item>
                </el-collapse>
                <el-empty v-if="getFilteredSectionBlocks(child.items).length === 0" description="暂无可配置项" />
              </div>
            </el-tab-pane>
          </el-tabs>
        </template>

        <template v-else-if="configMode === 'quick'">
          <div v-if="!topMenu.children || topMenu.children.length === 0" class="quick-wrapper">
            <div
              v-for="group in getFilteredQuickGroups(topMenu.items)"
              :key="group.key"
              class="quick-card"
            >
              <div class="quick-card-header">
                <div class="quick-title-block">
                  <div class="quick-title">{{ group.name }}</div>
                  <div class="quick-sub-title">{{ group.section }} | {{ group.field }}</div>
                </div>
                <el-button type="text" size="mini" @click="clearGroupMapping(group)">清空</el-button>
              </div>
                    <div v-for="row in group.rows" :key="row.code" class="quick-row">
                      <div class="quick-device-name">{{ row.deviceName || '默认映射' }}</div>
                      <el-input
                        v-model.trim="row.point"
                        :readonly="!!row.deviceId"
                        :placeholder="row.deviceId ? '点击选择点位' : '请输入点位'"
                        clearable
                        class="quick-input"
                        @focus="handlePointInputFocus(row)"
                        @mousedown.native.prevent="handlePointInputFocus(row)"
                      />
                      <el-input v-model.trim="row.fixedValue" placeholder="固定展示值" clearable class="quick-input" />
                      <el-checkbox v-model="row.useFixedDisplay" :true-label="1" :false-label="0">固定展示</el-checkbox>
                    </div>
            </div>
            <el-empty v-if="getFilteredQuickGroups(topMenu.items).length === 0" description="暂无可配置项" />
          </div>

          <el-tabs
            v-else
            v-model="activeChildMap[topMenu.code]"
            class="child-menu-tabs"
            type="border-card"
          >
            <el-tab-pane
              v-for="child in topMenu.children"
              :key="child.code"
              :label="child.name"
              :name="child.code"
            >
              <div class="quick-wrapper">
                <div
                  v-for="group in getFilteredQuickGroups(child.items)"
                  :key="group.key"
                  class="quick-card"
                >
                  <div class="quick-card-header">
                    <div class="quick-title-block">
                      <div class="quick-title">{{ group.name }}</div>
                      <div class="quick-sub-title">{{ group.section }} | {{ group.field }}</div>
                    </div>
                    <el-button type="text" size="mini" @click="clearGroupMapping(group)">清空</el-button>
                  </div>
                        <div v-for="row in group.rows" :key="row.code" class="quick-row">
                          <div class="quick-device-name">{{ row.deviceName || '默认映射' }}</div>
                          <el-input
                            v-model.trim="row.point"
                            :readonly="!!row.deviceId"
                            :placeholder="row.deviceId ? '点击选择点位' : '请输入点位'"
                            clearable
                            class="quick-input"
                            @focus="handlePointInputFocus(row)"
                            @mousedown.native.prevent="handlePointInputFocus(row)"
                          />
                          <el-input v-model.trim="row.fixedValue" placeholder="固定展示值" clearable class="quick-input" />
                          <el-checkbox v-model="row.useFixedDisplay" :true-label="1" :false-label="0">固定展示</el-checkbox>
                        </div>
                </div>
                <el-empty v-if="getFilteredQuickGroups(child.items).length === 0" description="暂无可配置项" />
              </div>
            </el-tab-pane>
          </el-tabs>
        </template>

        <el-table
          v-else-if="!topMenu.children || topMenu.children.length === 0"
          class="common-table"
          :data="topMenu.items"
          stripe
        >
          <el-table-column prop="section" label="分组" min-width="180" />
          <el-table-column prop="name" label="页面展示名称" min-width="280" />
          <el-table-column prop="field" label="字段名" min-width="260" />
          <el-table-column prop="deviceName" label="设备" min-width="220">
            <template slot-scope="scope">
              <span>{{ scope.row.deviceName || '-' }}</span>
            </template>
          </el-table-column>
          <el-table-column label="点位" min-width="300">
            <template slot-scope="scope">
              <el-input
                v-model.trim="scope.row.point"
                :readonly="!!scope.row.deviceId"
                :placeholder="scope.row.deviceId ? '点击选择点位' : '请输入点位'"
                clearable
                class="short-input"
                @focus="handlePointInputFocus(scope.row)"
                @mousedown.native.prevent="handlePointInputFocus(scope.row)"
              />
            </template>
          </el-table-column>
          <el-table-column label="固定展示值" min-width="300">
            <template slot-scope="scope">
              <el-input
                v-model.trim="scope.row.fixedValue"
                placeholder="请输入固定展示值"
                clearable
                class="short-input"
              />
            </template>
          </el-table-column>
          <el-table-column label="固定展示" width="120" align="center">
            <template slot-scope="scope">
              <el-checkbox v-model="scope.row.useFixedDisplay" :true-label="1" :false-label="0" />
            </template>
          </el-table-column>
          <el-table-column label="操作" width="120" fixed="right">
            <template slot-scope="scope">
              <el-button
                type="text"
                size="small"
                :disabled="!!scope.row.deviceId"
                @click="handleDeleteItem(scope.$index, topMenu.items)"
              >
                删除
              </el-button>
            </template>
          </el-table-column>
        </el-table>

        <el-tabs
          v-else
          v-model="activeChildMap[topMenu.code]"
          class="child-menu-tabs"
          type="border-card"
        >
          <el-tab-pane
            v-for="child in topMenu.children"
            :key="child.code"
            :label="child.name"
            :name="child.code"
          >
            <el-table class="common-table" :data="child.items" stripe>
              <el-table-column prop="section" label="分组" min-width="180" />
              <el-table-column prop="name" label="页面展示名称" min-width="280" />
              <el-table-column prop="field" label="字段名" min-width="260" />
              <el-table-column prop="deviceName" label="设备" min-width="220">
                <template slot-scope="scope">
                  <span>{{ scope.row.deviceName || '-' }}</span>
                </template>
              </el-table-column>
              <el-table-column label="点位" min-width="300">
                <template slot-scope="scope">
                  <el-input
                    v-model.trim="scope.row.point"
                    :readonly="!!scope.row.deviceId"
                    :placeholder="scope.row.deviceId ? '点击选择点位' : '请输入点位'"
                    clearable
                    class="short-input"
                    @focus="handlePointInputFocus(scope.row)"
                    @mousedown.native.prevent="handlePointInputFocus(scope.row)"
                  />
                </template>
              </el-table-column>
              <el-table-column label="固定展示值" min-width="300">
                <template slot-scope="scope">
                  <el-input
                    v-model.trim="scope.row.fixedValue"
                    placeholder="请输入固定展示值"
                    clearable
                    class="short-input"
                  />
                </template>
              </el-table-column>
              <el-table-column label="固定展示" width="120" align="center">
                <template slot-scope="scope">
                  <el-checkbox v-model="scope.row.useFixedDisplay" :true-label="1" :false-label="0" />
                </template>
              </el-table-column>
              <el-table-column label="操作" width="120" fixed="right">
                <template slot-scope="scope">
                  <el-button
                    type="text"
                    size="small"
                    :disabled="!!scope.row.deviceId"
                    @click="handleDeleteItem(scope.$index, child.items)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </el-tab-pane>
        </el-tabs>
      </el-tab-pane>

      <el-tab-pane label="状态枚举映射" name="workStatusEnum">
        <el-card class="enum-mapping-card" shadow="never">
          <div slot="header" class="enum-mapping-header">
            <span>状态枚举映射</span>
            <div>
              <el-button type="text" size="mini" @click="resetWorkStatusEnumMappings">恢复默认</el-button>
              <el-button type="text" size="mini" @click="addWorkStatusEnumMapping">新增映射</el-button>
            </div>
          </div>
          <el-radio-group v-model="activeEnumScope" size="mini" class="enum-scope-group">
            <el-radio-button v-for="scope in enumScopeOptions" :key="scope.key" :label="scope.key">{{ scope.label }}</el-radio-button>
          </el-radio-group>
          <el-table class="common-table" :data="getEnumMappingsByScope(activeEnumScope)" size="mini" stripe>
            <el-table-column label="系统状态编码" width="130">
              <template slot-scope="scope">
                <el-input v-model.trim="scope.row.enumCode" placeholder="如 3" />
              </template>
            </el-table-column>
            <el-table-column label="系统状态名称" width="160">
              <template slot-scope="scope">
                <el-input v-model.trim="scope.row.enumName" placeholder="如 待机" />
              </template>
            </el-table-column>
            <el-table-column label="站点上送值" width="180">
              <template slot-scope="scope">
                <el-input v-model.trim="scope.row.dataEnumCode" placeholder="如 1 / 3 / 4" />
              </template>
            </el-table-column>
            <el-table-column label="描述" min-width="220">
              <template slot-scope="scope">
                <el-input v-model.trim="scope.row.enumDesc" placeholder="可选描述" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="90" align="center">
              <template slot-scope="scope">
                <el-button type="text" size="mini" @click="removeWorkStatusEnumMapping(scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div class="enum-mapping-tip">说明：按上方状态项分别维护“站点上送值 -> 系统状态编码/名称”的映射关系。</div>
        </el-card>
      </el-tab-pane>
    </el-tabs>
    <el-dialog
      title="选择点位"
      :visible.sync="pointSelectorVisible"
      width="900px"
      :close-on-click-modal="false"
    >
      <el-form :inline="true" class="select-container">
        <el-form-item label="点位ID">
          <el-input v-model.trim="pointSelectorQuery.pointId" clearable placeholder="请输入点位ID（支持模糊）" style="width: 180px" />
        </el-form-item>
        <el-form-item label="点位描述">
          <el-input v-model.trim="pointSelectorQuery.pointDesc" clearable placeholder="请输入点位描述" style="width: 180px" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="searchPointSelector">搜索</el-button>
          <el-button @click="resetPointSelector">重置</el-button>
        </el-form-item>
      </el-form>

      <el-table
        v-loading="pointSelectorLoading"
        :data="pointSelectorList"
        class="common-table"
        stripe
        max-height="460px"
      >
        <el-table-column prop="pointId" label="点位ID" min-width="260" />
        <el-table-column prop="pointName" label="点位名称" min-width="160" />
        <el-table-column prop="pointDesc" label="点位描述" min-width="220" />
        <el-table-column prop="deviceCategory" label="设备类型" width="120" />
        <el-table-column prop="deviceId" label="设备ID" min-width="140" />
        <el-table-column label="操作" width="90" align="center" fixed="right">
          <template slot-scope="scope">
            <el-button type="text" size="mini" @click="selectPointFromDialog(scope.row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>

      <el-pagination
        v-show="pointSelectorTotal > 0"
        background
        layout="total, prev, pager, next"
        :current-page="pointSelectorQuery.pageNum"
        :page-size="pointSelectorQuery.pageSize"
        :total="pointSelectorTotal"
        style="margin-top: 12px; text-align: center"
        @current-change="handlePointSelectorPageChange"
      />
    </el-dialog>
    <el-dialog
      title="初始化单体电池配置"
      :visible.sync="singleBatteryInitDialogVisible"
      width="560px"
      append-to-body
      class="ems-dialog"
    >
      <el-form label-width="110px" size="small">
        <el-form-item label="电池堆">
          <el-select v-model="singleBatteryInitForm.stackDeviceId" placeholder="请选择电池堆" style="width: 100%" @change="handleSingleBatteryStackChange">
            <el-option v-for="item in singleBatteryStackOptions" :key="item.id || item.deviceId || item" :label="item.name || item.deviceName || item.id || item.deviceId || item" :value="item.id || item.deviceId || item" />
          </el-select>
        </el-form-item>
        <el-form-item label="电池簇">
          <el-select v-model="singleBatteryInitForm.clusterDeviceId" placeholder="请选择电池簇" style="width: 100%">
            <el-option v-for="item in singleBatteryClusterOptions" :key="item.id || item.deviceId || item" :label="item.name || item.deviceName || item.id || item.deviceId || item" :value="item.id || item.deviceId || item" />
          </el-select>
        </el-form-item>
        <el-form-item label="目标数量">
          <el-input-number v-model="singleBatteryInitForm.targetCount" :min="1" :precision="0" controls-position="right" style="width: 220px" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="singleBatteryInitDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="singleBatteryInitLoading" @click="submitSingleBatteryInit">确定初始化</el-button>
      </div>
    </el-dialog>
    <el-dialog
      title="初始化结果"
      :visible.sync="singleBatteryInitResultVisible"
      width="980px"
      append-to-body
      class="ems-dialog"
    >
      <el-alert
        :title="singleBatteryInitResult.message || '初始化完成'"
        :type="singleBatteryInitResult.committed ? 'success' : 'warning'"
        :closable="false"
        style="margin-bottom: 16px;"
      />
      <el-descriptions :column="3" border size="small">
        <el-descriptions-item label="站点ID">{{ singleBatteryInitResult.siteId || '-' }}</el-descriptions-item>
        <el-descriptions-item label="初始化范围">{{ singleBatteryInitResult.scopeType === 'stack' ? '电池堆' : '电池簇' }}</el-descriptions-item>
        <el-descriptions-item label="范围设备ID">{{ singleBatteryInitResult.scopeDeviceId || '-' }}</el-descriptions-item>
        <el-descriptions-item label="目标数量">{{ singleBatteryInitResult.targetCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="现有单体">{{ singleBatteryInitResult.existingBatteryCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="已初始化单体">{{ singleBatteryInitResult.initializedBatteryCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="新增单体">{{ singleBatteryInitResult.insertedBatteryCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="命中点位">{{ singleBatteryInitResult.pointHitCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="固定值回填">{{ singleBatteryInitResult.fixedValueFallbackCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="新增映射">{{ singleBatteryInitResult.insertedMappingCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="更新映射">{{ singleBatteryInitResult.updatedMappingCount || 0 }}</el-descriptions-item>
        <el-descriptions-item label="是否成功">{{ singleBatteryInitResult.committed ? '是' : '否' }}</el-descriptions-item>
      </el-descriptions>
    </el-dialog>
  </div>
</template>

<script>
import {
  getPointMatchList,
  getSingleMonitorProjectPointMapping,
  getSingleMonitorWorkStatusEnumMappings,
  initializeSingleBatteryMonitorMappings,
  saveSingleMonitorProjectPointMapping,
  saveSingleMonitorWorkStatusEnumMappings
} from '@/api/ems/site'
import { getClusterNameList, getStackNameList } from '@/api/ems/dzjk'

export default {
  name: 'MonitorPointMapping',
  data() {
    return {
      siteId: '',
      siteName: '',
      activeConfigTab: 'point',
      activeEnumScope: 'PCS|workStatus',
      enumScopeOptions: [
        { key: 'PCS|workStatus', label: 'PCS-工作状态' },
        { key: 'PCS|gridStatus', label: 'PCS-并网状态' },
        { key: 'PCS|deviceStatus', label: 'PCS-设备状态' },
        { key: 'PCS|controlMode', label: 'PCS-控制模式' },
        { key: 'STACK|workStatus', label: 'BMS总览-工作状态' },
        { key: 'STACK|pcsCommunicationStatus', label: 'BMS总览-与PCS通信' },
        { key: 'STACK|emsCommunicationStatus', label: 'BMS总览-与EMS通信' },
        { key: 'CLUSTER|workStatus', label: 'BMS电池簇-工作状态' },
        { key: 'CLUSTER|pcsCommunicationStatus', label: 'BMS电池簇-与PCS通信' },
        { key: 'CLUSTER|emsCommunicationStatus', label: 'BMS电池簇-与EMS通信' }
      ],
      configMode: 'table',
      quickFilter: '',
      activeTopMenu: 'HOME',
      activeChildMap: {},
      sectionActiveMap: {},
      topMenuList: [],
      deletedFieldCodes: [],
      currentPointRow: null,
      pointSelectorVisible: false,
      pointSelectorLoading: false,
      pointSelectorTotal: 0,
      pointSelectorList: [],
      pointSelectorQuery: {
        pageNum: 1,
        pageSize: 10,
        siteId: '',
        deviceCategory: '',
        deviceId: '',
        pointId: '',
        pointDesc: ''
      },
      autoSaveTimer: null,
      autoSaveDelay: 900,
      pointSelectorQueryTimer: null,
      pointSelectorQueryDelay: 280,
      suppressAutoSave: false,
      isSaving: false,
      saveStatusText: '自动保存已开启',
      singleBatteryInitLoading: false,
      singleBatteryInitDialogVisible: false,
      singleBatteryInitResultVisible: false,
      singleBatteryStackOptions: [],
      singleBatteryClusterOptions: [],
      singleBatteryInitForm: {
        stackDeviceId: '',
        clusterDeviceId: '',
        targetCount: 1
      },
      singleBatteryInitResult: {
        committed: false,
        siteId: '',
        scopeType: 'stack',
        scopeDeviceId: '',
        targetCount: 0,
        existingBatteryCount: 0,
        initializedBatteryCount: 0,
        insertedBatteryCount: 0,
        pointHitCount: 0,
        fixedValueFallbackCount: 0,
        insertedMappingCount: 0,
        updatedMappingCount: 0,
        message: ''
      },
      lastSavedPointSignature: '',
      lastSavedEnumSignature: '',
      workStatusEnumMappings: []
    }
  },
  watch: {
    '$route.query.siteId': {
      immediate: false,
      async handler(newSiteId) {
        if (newSiteId === this.siteId) {
          return
        }
        this.siteId = newSiteId || ''
        this.siteName = this.$route.query.siteName || this.siteId
        await this.initMenuStructure()
      }
    },
    topMenuList: {
      deep: true,
      handler() {
        this.scheduleAutoSave()
      }
    },
    deletedFieldCodes: {
      deep: true,
      handler() {
        this.scheduleAutoSave()
      }
    },
    workStatusEnumMappings: {
      deep: true,
      handler() {
        this.scheduleAutoSave()
      }
    },
    'pointSelectorQuery.pointId'() {
      this.schedulePointSelectorSearch()
    },
    'pointSelectorQuery.pointDesc'() {
      this.schedulePointSelectorSearch()
    }
  },
  computed: {
    showSingleBatteryImportActions() {
      const currentTopMenu = this.topMenuList.find(item => item.code === this.activeTopMenu)
      if (!currentTopMenu) {
        return false
      }
      if (currentTopMenu.code === 'SBJK_DTDC') {
        return true
      }
      const activeChildCode = this.activeChildMap[this.activeTopMenu] || ''
      return activeChildCode === 'SBJK_DTDC'
    }
  },
  methods: {
    async initMenuStructure() {
      if (!this.siteId) {
        this.topMenuList = []
        this.deletedFieldCodes = []
        this.workStatusEnumMappings = this.buildDefaultWorkStatusEnumMappings()
        this.activeEnumScope = this.enumScopeOptions[0]?.key || 'PCS|workStatus'
        return
      }
      this.suppressAutoSave = true
      const [mappingResp, enumResp] = await Promise.all([
        getSingleMonitorProjectPointMapping(this.siteId),
        getSingleMonitorWorkStatusEnumMappings(this.siteId)
      ])
      const list = mappingResp?.data || []
      const enumList = Array.isArray(enumResp?.data) ? enumResp.data : []
      const topMap = new Map()
      const childMap = new Map()

      list.forEach((row, index) => {
        const moduleCode = row.moduleCode || 'UNKNOWN'
        const moduleName = row.moduleName || '未分组'
        const menuCode = row.menuCode || moduleCode
        const menuName = row.menuName || moduleName
        const topKey = moduleCode
        if (!topMap.has(topKey)) {
          topMap.set(topKey, {
            code: moduleCode,
            name: moduleName,
            children: [],
            items: []
          })
        }
        const item = {
          code: `${moduleCode}_${menuCode}_${row.fieldCode || index}_${row.deviceId || 'NA'}`,
          section: row.sectionName || '-',
          name: row.fieldName || '-',
          field: row.fieldCode || '',
          deviceId: row.deviceId || '',
          deviceName: row.deviceName || '',
          point: row.dataPoint || '',
          fixedValue: row.fixedDataPoint || '',
          useFixedDisplay: row.useFixedDisplay === 1 ? 1 : 0
        }
        const topItem = topMap.get(topKey)
        const isTopDirect = moduleCode === menuCode || moduleCode === 'HOME'
        if (isTopDirect) {
          topItem.items.push(item)
          return
        }
        const childKey = `${moduleCode}_${menuCode}`
        if (!childMap.has(childKey)) {
          const child = { code: menuCode, name: menuName, items: [] }
          childMap.set(childKey, child)
          topItem.children.push(child)
        }
        childMap.get(childKey).items.push(item)
      })

      this.topMenuList = Array.from(topMap.values())
      const firstTop = this.topMenuList[0]
      this.activeTopMenu = firstTop ? firstTop.code : 'HOME'
      this.activeChildMap = {}
      this.sectionActiveMap = {}
      this.deletedFieldCodes = []
      this.workStatusEnumMappings = this.normalizeWorkStatusEnumMappings(enumList)
      if (!this.enumScopeOptions.find(item => item.key === this.activeEnumScope)) {
        this.activeEnumScope = this.enumScopeOptions[0]?.key || 'PCS|workStatus'
      }
      this.topMenuList.forEach(top => {
        if (top.children && top.children.length > 0) {
          this.$set(this.activeChildMap, top.code, top.children[0].code)
        }
      })
      this.$nextTick(() => {
        this.lastSavedPointSignature = this.buildPointPayloadSignature()
        this.lastSavedEnumSignature = this.buildEnumPayloadSignature()
        this.suppressAutoSave = false
        this.saveStatusText = '自动保存已开启'
      })
    },
    getAllMappingRows() {
      const rows = []
      this.topMenuList.forEach(top => {
        ;(top.items || []).forEach(item => rows.push(item))
        ;(top.children || []).forEach(child => {
          ;(child.items || []).forEach(item => rows.push(item))
        })
      })
      return rows
    },
    getDeviceCategoryByField(fieldCode) {
      if (!fieldCode || fieldCode.indexOf('__') === -1) {
        return ''
      }
      const menuCode = fieldCode.split('__')[0]
      const menuMap = {
        SBJK_SSYX: 'PCS',
        SBJK_EMS: 'EMS',
        SBJK_PCS: 'PCS',
        SBJK_BMSZL: 'STACK',
        SBJK_BMSDCC: 'CLUSTER',
        SBJK_DTDC: 'BATTERY',
        SBJK_DB: 'AMMETER',
        SBJK_YL: 'COOLING',
        SBJK_DH: 'DH',
        SBJK_XF: 'XF'
      }
      return menuMap[menuCode] || ''
    },
    handlePointInputFocus(row) {
      if (!row) {
        return
      }
      this.currentPointRow = row
      this.pointSelectorQuery.pageNum = 1
      this.pointSelectorQuery.siteId = this.siteId
      this.pointSelectorQuery.deviceCategory = ''
      this.pointSelectorQuery.deviceId = ''
      this.pointSelectorVisible = true
      this.loadPointSelectorList()
    },
    selectPointFromDialog(pointRow) {
      if (!this.currentPointRow) {
        return
      }
      this.currentPointRow.point = pointRow?.pointId || ''
      this.currentPointRow.useFixedDisplay = 0
      this.currentPointRow = null
      this.pointSelectorVisible = false
    },
    searchPointSelector() {
      this.pointSelectorQuery.pageNum = 1
      this.loadPointSelectorList()
    },
    resetPointSelector() {
      this.pointSelectorQuery.pageNum = 1
      this.pointSelectorQuery.pointId = ''
      this.pointSelectorQuery.pointDesc = ''
      this.loadPointSelectorList()
    },
    handlePointSelectorPageChange(pageNum) {
      this.pointSelectorQuery.pageNum = pageNum
      this.loadPointSelectorList()
    },
    openImportConfigDialog() {
      if (!this.siteId) {
        this.$message.warning('请先选择站点')
        return
      }
      if (this.$refs.configInput) {
        this.$refs.configInput.value = ''
        this.$refs.configInput.click()
      }
    },
    async openSingleBatteryInitDialog() {
      if (!this.siteId) {
        this.$message.warning('请先选择站点')
        return
      }
      this.singleBatteryInitDialogVisible = true
      this.singleBatteryInitForm.stackDeviceId = ''
      this.singleBatteryInitForm.clusterDeviceId = ''
      this.singleBatteryInitForm.targetCount = 1
      await this.loadSingleBatteryStackOptions()
      this.singleBatteryClusterOptions = []
    },
    async loadSingleBatteryStackOptions() {
      const response = await getStackNameList(this.siteId)
      this.singleBatteryStackOptions = Array.isArray(response?.data) ? response.data : []
    },
    async handleSingleBatteryStackChange(stackDeviceId) {
      this.singleBatteryInitForm.clusterDeviceId = ''
      if (!stackDeviceId) {
        this.singleBatteryClusterOptions = []
        return
      }
      const response = await getClusterNameList({ stackDeviceId, siteId: this.siteId })
      this.singleBatteryClusterOptions = Array.isArray(response?.data) ? response.data : []
    },
    normalizeSingleBatteryInitResult(result) {
      const source = result || {}
      return {
        committed: !!source.committed,
        siteId: source.siteId || '',
        scopeType: source.scopeType || 'stack',
        scopeDeviceId: source.scopeDeviceId || '',
        targetCount: Number(source.targetCount || 0),
        existingBatteryCount: Number(source.existingBatteryCount || 0),
        initializedBatteryCount: Number(source.initializedBatteryCount || 0),
        insertedBatteryCount: Number(source.insertedBatteryCount || 0),
        pointHitCount: Number(source.pointHitCount || 0),
        fixedValueFallbackCount: Number(source.fixedValueFallbackCount || 0),
        insertedMappingCount: Number(source.insertedMappingCount || 0),
        updatedMappingCount: Number(source.updatedMappingCount || 0),
        message: source.message || ''
      }
    },
    async submitSingleBatteryInit() {
      if (!this.siteId) {
        this.$message.warning('请先选择站点')
        return
      }
      if (!this.singleBatteryInitForm.stackDeviceId) {
        this.$message.warning('请选择电池堆')
        return
      }
      if (!this.singleBatteryInitForm.clusterDeviceId) {
        this.$message.warning('请选择电池簇')
        return
      }
      const scopeType = 'cluster'
      const scopeDeviceId = this.singleBatteryInitForm.clusterDeviceId
      this.singleBatteryInitLoading = true
      try {
        const response = await initializeSingleBatteryMonitorMappings({
          siteId: this.siteId,
          scopeType,
          scopeDeviceId,
          targetCount: this.singleBatteryInitForm.targetCount
        })
        this.singleBatteryInitResult = this.normalizeSingleBatteryInitResult(response?.data || {})
        this.singleBatteryInitDialogVisible = false
        this.singleBatteryInitResultVisible = true
        if (this.singleBatteryInitResult.committed) {
          await this.initMenuStructure()
          this.$message.success('初始化单体电池配置成功')
        } else {
          this.$message.warning(this.singleBatteryInitResult.message || '初始化未完成')
        }
      } catch (error) {
        this.$message.error(error?.message || '初始化失败，请稍后重试')
      } finally {
        this.singleBatteryInitLoading = false
      }
    },
    handleConfigFileChange(event) {
      const file = event && event.target && event.target.files && event.target.files[0]
      if (!file) {
        return
      }
      const isCsv = /\.csv$/i.test(file.name || '')
      if (!isCsv) {
        this.$message.error('仅支持导入 CSV 文件')
        return
      }
      const reader = new FileReader()
      reader.onload = async () => {
        const text = (reader.result || '').toString()
        const parsed = this.parseCsvImportData(text)
        if (!parsed) {
          this.$message.error('CSV 格式不正确，导入失败')
          return
        }
        await this.confirmAndApplyImport(parsed, file.name || '配置文件')
      }
      reader.onerror = () => {
        this.$message.error('读取文件失败，请重试')
      }
      reader.readAsText(file, 'utf-8')
    },
    async confirmAndApplyImport(parsedData, fileName) {
      if (!this.siteId) {
        this.$message.warning('请先选择站点')
        return
      }
      const importedSiteId = (((parsedData || {}).meta || {}).siteId || (parsedData || {}).siteId || '').toString().trim()
      if (importedSiteId && importedSiteId !== this.siteId) {
        try {
          await this.$confirm(
            `文件站点(${importedSiteId})与当前站点(${this.siteId})不一致，仍要导入并覆盖当前配置吗？`,
            '提示',
            {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning',
              showClose: false,
              closeOnClickModal: false
            }
          )
        } catch (error) {
          return
        }
      } else {
        try {
          await this.$confirm(
            `确认导入文件 ${fileName} 并覆盖当前页面配置吗？`,
            '提示',
            {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning',
              showClose: false,
              closeOnClickModal: false
            }
          )
        } catch (error) {
          return
        }
      }
      this.applyImportedConfig(parsedData)
    },
    normalizeImportMappings(parsedData) {
      const data = parsedData || {}
      const mappings = []
      if (Array.isArray(data.mappings)) {
        return data.mappings
      }
      if (data.pointMapping && Array.isArray(data.pointMapping.mappings)) {
        return data.pointMapping.mappings
      }
      if (data.pointPayload && Array.isArray(data.pointPayload.mappings)) {
        return data.pointPayload.mappings
      }
      return mappings
    },
    normalizeImportDeletedFieldCodes(parsedData) {
      const data = parsedData || {}
      const source = data.deletedFieldCodes || ((data.pointMapping || {}).deletedFieldCodes) || ((data.pointPayload || {}).deletedFieldCodes) || []
      if (!Array.isArray(source)) {
        return []
      }
      return source
        .map(item => (item === null || item === undefined ? '' : String(item).trim()))
        .filter(item => !!item)
    },
    normalizeImportEnumMappings(parsedData) {
      const data = parsedData || {}
      if (Array.isArray(data.enumMappings)) {
        return data.enumMappings
      }
      if (data.workStatusEnum && Array.isArray(data.workStatusEnum.mappings)) {
        return data.workStatusEnum.mappings
      }
      return []
    },
    escapeCsvCell(value) {
      if (value === null || value === undefined) {
        return ''
      }
      const text = String(value)
      if (/[",\n\r]/.test(text)) {
        return `"${text.replace(/"/g, '""')}"`
      }
      return text
    },
    parseCsvText(text) {
      if (!text) {
        return []
      }
      const normalized = String(text).replace(/^\uFEFF/, '')
      const rows = []
      let row = []
      let cell = ''
      let inQuotes = false
      for (let i = 0; i < normalized.length; i += 1) {
        const ch = normalized[i]
        if (inQuotes) {
          if (ch === '"') {
            if (normalized[i + 1] === '"') {
              cell += '"'
              i += 1
            } else {
              inQuotes = false
            }
          } else {
            cell += ch
          }
          continue
        }
        if (ch === '"') {
          inQuotes = true
          continue
        }
        if (ch === ',') {
          row.push(cell)
          cell = ''
          continue
        }
        if (ch === '\n') {
          row.push(cell)
          rows.push(row)
          row = []
          cell = ''
          continue
        }
        if (ch === '\r') {
          if (normalized[i + 1] === '\n') {
            continue
          }
          row.push(cell)
          rows.push(row)
          row = []
          cell = ''
          continue
        }
        cell += ch
      }
      row.push(cell)
      if (row.some(item => item !== '')) {
        rows.push(row)
      }
      return rows
    },
    mapCsvHeaderIndexes(headers) {
      const headerMap = {}
      const aliasMap = {
        type: ['type'],
        siteId: ['siteId'],
        siteName: ['siteName'],
        exportedAt: ['exportedAt'],
        fieldCode: ['fieldCode'],
        deviceId: ['deviceId'],
        dataPoint: ['dataPoint', 'pointId'],
        fixedDataPoint: ['fixedDataPoint'],
        useFixedDisplay: ['useFixedDisplay'],
        deletedFieldCode: ['deletedFieldCode'],
        deviceCategory: ['deviceCategory'],
        matchField: ['matchField'],
        enumCode: ['enumCode'],
        enumName: ['enumName'],
        dataEnumCode: ['dataEnumCode'],
        enumDesc: ['enumDesc']
      }
      const normalized = headers.map(item => String(item || '').trim())
      Object.keys(aliasMap).forEach(key => {
        const aliases = aliasMap[key]
        const index = normalized.findIndex(item => aliases.includes(item))
        if (index >= 0) {
          headerMap[key] = index
        }
      })
      return headerMap
    },
    parseCsvImportData(text) {
      const rows = this.parseCsvText(text)
      if (!rows.length) {
        return null
      }
      const headerIndexes = this.mapCsvHeaderIndexes(rows[0] || [])
      if (Object.keys(headerIndexes).length === 0) {
        return null
      }
      const meta = {}
      const mappings = []
      const deletedFieldCodes = []
      const parseUseFixedDisplay = value => {
        const textValue = String(value || '').trim().toLowerCase()
        if (['1', 'true', '是', 'y', 'yes'].includes(textValue)) {
          return 1
        }
        return 0
      }
      for (let i = 1; i < rows.length; i += 1) {
        const row = rows[i]
        if (!row || row.every(cell => String(cell || '').trim() === '')) {
          continue
        }
        const getValue = key => {
          const index = headerIndexes[key]
          if (index === undefined) {
            return ''
          }
          return row[index] === undefined || row[index] === null ? '' : String(row[index]).trim()
        }
        const type = getValue('type').toLowerCase()
        const siteId = getValue('siteId')
        const siteName = getValue('siteName')
        const exportedAt = getValue('exportedAt')
        if (type === 'meta' || (!type && (siteId || siteName || exportedAt))) {
          if (siteId) {
            meta.siteId = siteId
          }
          if (siteName) {
            meta.siteName = siteName
          }
          if (exportedAt) {
            meta.exportedAt = exportedAt
          }
          continue
        }
        const fieldCode = getValue('fieldCode')
        const deviceId = getValue('deviceId')
        const dataPoint = getValue('dataPoint')
        const fixedDataPoint = getValue('fixedDataPoint')
        const useFixedDisplay = parseUseFixedDisplay(getValue('useFixedDisplay'))
        const deletedFieldCode = getValue('deletedFieldCode')
        if (type === 'deleted' || (!type && deletedFieldCode && !fieldCode)) {
          if (deletedFieldCode) {
            deletedFieldCodes.push(deletedFieldCode)
          }
          continue
        }
        if (type === 'point' || fieldCode) {
          if (!fieldCode) {
            continue
          }
          mappings.push({
            fieldCode,
            deviceId,
            dataPoint,
            fixedDataPoint,
            useFixedDisplay
          })
        }
      }
      return {
        meta,
        mappings,
        deletedFieldCodes
      }
    },
    buildMappingKey(fieldCode, deviceId) {
      return `${(fieldCode || '').toString().trim()}::${(deviceId || '').toString().trim()}`
    },
    applyImportedConfig(parsedData) {
      const importedMappings = this.normalizeImportMappings(parsedData)
      const importedDeletedFieldCodes = this.normalizeImportDeletedFieldCodes(parsedData)
      if (!Array.isArray(importedMappings) || importedMappings.length === 0) {
        this.$message.error('导入文件缺少 mappings 数据')
        return
      }
      const mappingByKey = new Map()
      importedMappings.forEach(item => {
        const fieldCode = ((item && item.fieldCode) || '').toString().trim()
        if (!fieldCode) {
          return
        }
        const deviceId = ((item && item.deviceId) || '').toString().trim()
        const key = this.buildMappingKey(fieldCode, deviceId)
        mappingByKey.set(key, {
          dataPoint: ((item && item.dataPoint) || '').toString(),
          fixedDataPoint: ((item && item.fixedDataPoint) || '').toString(),
          useFixedDisplay: item && item.useFixedDisplay === 1 ? 1 : 0
        })
      })
      const allRows = this.getAllMappingRows()
      let hitCount = 0
      allRows.forEach(row => {
        const key = this.buildMappingKey(row && row.field, row && row.deviceId)
        const mapped = mappingByKey.get(key)
        if (mapped) {
          row.point = mapped.dataPoint
          row.fixedValue = mapped.fixedDataPoint
          row.useFixedDisplay = mapped.useFixedDisplay
          hitCount += 1
        } else {
          row.point = ''
          row.fixedValue = ''
          row.useFixedDisplay = 0
        }
      })
      this.suppressAutoSave = true
      this.deletedFieldCodes = importedDeletedFieldCodes
      this.$nextTick(() => {
        this.suppressAutoSave = false
        this.scheduleAutoSave()
      })
      const missCount = Math.max(mappingByKey.size - hitCount, 0)
      if (missCount > 0) {
        this.$message.warning(`导入完成：匹配 ${hitCount} 条，忽略 ${missCount} 条（当前页面无对应字段/设备）`)
      } else {
        this.$message.success(`导入完成：匹配 ${hitCount} 条，系统将自动保存`)
      }
    },
    escapeJsonValue(value) {
      if (value === undefined || value === null) {
        return ''
      }
      return String(value)
    },
    exportConfig() {
      if (!this.siteId) {
        this.$message.warning('请先选择站点')
        return
      }
      const pointPayload = this.buildSavePayload()
      const headers = [
        'type',
        'siteId',
        'siteName',
        'exportedAt',
        'fieldCode',
        'deviceId',
        'dataPoint',
        'fixedDataPoint',
        'useFixedDisplay',
        'deletedFieldCode'
      ]
      const rows = []
      rows.push([
        'meta',
        this.siteId,
        this.siteName || '',
        new Date().toISOString(),
        '',
        '',
        '',
        '',
        '',
        ''
      ])
      ;(pointPayload.mappings || []).forEach(item => {
        rows.push([
          'point',
          this.siteId,
          this.siteName || '',
          '',
          item.fieldCode || '',
          item.deviceId || '',
          item.dataPoint || '',
          item.fixedDataPoint || '',
          item.useFixedDisplay === 1 ? '1' : '0',
          ''
        ])
      })
      ;(pointPayload.deletedFieldCodes || []).forEach(code => {
        rows.push([
          'deleted',
          this.siteId,
          this.siteName || '',
          '',
          '',
          '',
          '',
          '',
          '',
          code || ''
        ])
      })
      const csvText = [headers.map(cell => this.escapeCsvCell(cell)).join(','), ...rows.map(row => row.map(cell => this.escapeCsvCell(cell)).join(','))].join('\n')
      const blob = new Blob([`\uFEFF${csvText}`], { type: 'text/csv;charset=utf-8;' })
      const safeSiteId = this.escapeJsonValue(this.siteId).replace(/[^\w-]/g, '_')
      const fileName = `single_monitor_mapping_${safeSiteId || 'site'}_${Date.now()}.csv`
      const url = window.URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = fileName
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(url)
      this.$message.success('导出成功')
    },
    schedulePointSelectorSearch() {
      if (!this.pointSelectorVisible) {
        return
      }
      if (this.pointSelectorQueryTimer) {
        clearTimeout(this.pointSelectorQueryTimer)
      }
      this.pointSelectorQueryTimer = setTimeout(() => {
        this.pointSelectorQuery.pageNum = 1
        this.loadPointSelectorList()
      }, this.pointSelectorQueryDelay)
    },
    loadPointSelectorList(options = {}) {
      const { silent = false } = options
      if (!this.pointSelectorQuery.siteId) {
        this.pointSelectorList = []
        this.pointSelectorTotal = 0
        return
      }
      if (!silent) {
        this.pointSelectorLoading = true
      }
      const query = {
        ...this.pointSelectorQuery
      }
      delete query.deviceCategory
      delete query.deviceId
      getPointMatchList(query)
        .then(response => {
          this.pointSelectorList = response?.rows || []
          this.pointSelectorTotal = response?.total || 0
        })
        .finally(() => {
          if (!silent) {
            this.pointSelectorLoading = false
          }
        })
    },
    getFilteredQuickGroups(list) {
      const sourceList = Array.isArray(list) ? list : []
      const groupMap = new Map()
      sourceList.forEach(item => {
        const groupKey = `${item.section || '-'}__${item.field || '-'}__${item.name || '-'}`
        if (!groupMap.has(groupKey)) {
          groupMap.set(groupKey, {
            key: groupKey,
            section: item.section || '-',
            field: item.field || '-',
            name: item.name || '-',
            rows: []
          })
        }
        groupMap.get(groupKey).rows.push(item)
      })
      const keyword = (this.quickFilter || '').toLowerCase()
      const groups = Array.from(groupMap.values())
      if (!keyword) {
        return groups
      }
      return groups.filter(group => {
        const groupMatch = [group.section, group.field, group.name].some(text => (text || '').toLowerCase().includes(keyword))
        if (groupMatch) {
          return true
        }
        return group.rows.some(row => (row.deviceName || '').toLowerCase().includes(keyword))
      })
    },
    getFilteredSectionBlocks(list) {
      const fieldGroups = this.getFilteredQuickGroups(list)
      const sectionMap = new Map()
      fieldGroups.forEach(group => {
        const sectionName = group.section || '-'
        if (!sectionMap.has(sectionName)) {
          sectionMap.set(sectionName, {
            key: sectionName,
            section: sectionName,
            fields: []
          })
        }
        sectionMap.get(sectionName).fields.push(group)
      })
      return Array.from(sectionMap.values())
    },
    buildSectionPanelKey(topCode, childCode) {
      return `${topCode || 'ROOT'}__${childCode || 'ROOT'}`
    },
    getSectionActiveValue(panelKey, blocks) {
      if (Object.prototype.hasOwnProperty.call(this.sectionActiveMap, panelKey)) {
        return this.sectionActiveMap[panelKey]
      }
      return Array.isArray(blocks) && blocks.length > 0 ? blocks[0].key : ''
    },
    setSectionActiveValue(panelKey, value) {
      this.$set(this.sectionActiveMap, panelKey, value || '')
    },
    clearGroupMapping(group) {
      if (!group || !Array.isArray(group.rows)) {
        return
      }
      group.rows.forEach(row => {
        row.point = ''
        row.fixedValue = ''
        row.useFixedDisplay = 0
      })
      this.$message.success('已清空该字段映射，系统将自动保存')
    },
    buildDefaultWorkStatusEnumMappings() {
      return [
        this.buildEnumItem('PCS', 'workStatus', '0', '运行'),
        this.buildEnumItem('PCS', 'workStatus', '1', '停机'),
        this.buildEnumItem('PCS', 'workStatus', '2', '故障'),
        this.buildEnumItem('PCS', 'workStatus', '3', '待机'),
        this.buildEnumItem('PCS', 'workStatus', '4', '充电'),
        this.buildEnumItem('PCS', 'workStatus', '5', '放电'),
        this.buildEnumItem('PCS', 'gridStatus', '0', '并网'),
        this.buildEnumItem('PCS', 'gridStatus', '1', '未并网'),
        this.buildEnumItem('PCS', 'deviceStatus', '0', '离线'),
        this.buildEnumItem('PCS', 'deviceStatus', '1', '在线'),
        this.buildEnumItem('PCS', 'controlMode', '0', '远程'),
        this.buildEnumItem('PCS', 'controlMode', '1', '本地'),
        this.buildEnumItem('STACK', 'workStatus', '0', '静置'),
        this.buildEnumItem('STACK', 'workStatus', '1', '充电'),
        this.buildEnumItem('STACK', 'workStatus', '2', '放电'),
        this.buildEnumItem('STACK', 'workStatus', '3', '浮充'),
        this.buildEnumItem('STACK', 'workStatus', '4', '待机'),
        this.buildEnumItem('STACK', 'workStatus', '5', '运行'),
        this.buildEnumItem('STACK', 'workStatus', '9', '故障'),
        this.buildEnumItem('STACK', 'pcsCommunicationStatus', '0', '正常'),
        this.buildEnumItem('STACK', 'pcsCommunicationStatus', '1', '通讯中断'),
        this.buildEnumItem('STACK', 'pcsCommunicationStatus', '2', '异常'),
        this.buildEnumItem('STACK', 'emsCommunicationStatus', '0', '正常'),
        this.buildEnumItem('STACK', 'emsCommunicationStatus', '1', '通讯中断'),
        this.buildEnumItem('STACK', 'emsCommunicationStatus', '2', '异常'),
        this.buildEnumItem('CLUSTER', 'workStatus', '0', '静置'),
        this.buildEnumItem('CLUSTER', 'workStatus', '1', '充电'),
        this.buildEnumItem('CLUSTER', 'workStatus', '2', '放电'),
        this.buildEnumItem('CLUSTER', 'workStatus', '3', '待机'),
        this.buildEnumItem('CLUSTER', 'workStatus', '5', '运行'),
        this.buildEnumItem('CLUSTER', 'workStatus', '9', '故障'),
        this.buildEnumItem('CLUSTER', 'pcsCommunicationStatus', '0', '正常'),
        this.buildEnumItem('CLUSTER', 'pcsCommunicationStatus', '1', '通讯中断'),
        this.buildEnumItem('CLUSTER', 'pcsCommunicationStatus', '2', '异常'),
        this.buildEnumItem('CLUSTER', 'emsCommunicationStatus', '0', '正常'),
        this.buildEnumItem('CLUSTER', 'emsCommunicationStatus', '1', '通讯中断'),
        this.buildEnumItem('CLUSTER', 'emsCommunicationStatus', '2', '异常')
      ]
    },
    buildEnumItem(deviceCategory, matchField, enumCode = '', enumName = '', enumDesc = '', dataEnumCode = '') {
      return {
        deviceCategory: deviceCategory || '',
        matchField: matchField || '',
        enumCode: enumCode || '',
        enumName: enumName || '',
        enumDesc: enumDesc || '',
        dataEnumCode: dataEnumCode || ''
      }
    },
    parseEnumScopeKey(scopeKey) {
      const text = scopeKey || ''
      const splitIndex = text.indexOf('|')
      if (splitIndex < 0) {
        return { deviceCategory: '', matchField: '' }
      }
      return {
        deviceCategory: text.slice(0, splitIndex),
        matchField: text.slice(splitIndex + 1)
      }
    },
    getEnumMappingsByScope(scopeKey) {
      const scope = this.parseEnumScopeKey(scopeKey)
      return (this.workStatusEnumMappings || []).filter(item => {
        return (item.deviceCategory || '') === scope.deviceCategory && (item.matchField || '') === scope.matchField
      })
    },
    normalizeWorkStatusEnumMappings(list) {
      const source = Array.isArray(list) ? list : []
      const normalized = source
        .map(item => this.buildEnumItem(
          (item && item.deviceCategory) || '',
          (item && item.matchField) || '',
          (item && item.enumCode) || '',
          (item && item.enumName) || '',
          (item && item.enumDesc) || '',
          (item && item.dataEnumCode) || ''
        ))
        .filter(item => (item.deviceCategory && item.matchField) && (item.enumCode || item.enumName || item.dataEnumCode))
      if (normalized.length > 0) {
        return normalized
      }
      return this.buildDefaultWorkStatusEnumMappings()
    },
    addWorkStatusEnumMapping() {
      const scope = this.parseEnumScopeKey(this.activeEnumScope)
      this.workStatusEnumMappings.push(this.buildEnumItem(scope.deviceCategory, scope.matchField))
    },
    removeWorkStatusEnumMapping(row) {
      const index = this.workStatusEnumMappings.findIndex(item => item === row)
      if (index < 0) {
        return
      }
      this.workStatusEnumMappings.splice(index, 1)
    },
    resetWorkStatusEnumMappings() {
      this.workStatusEnumMappings = this.buildDefaultWorkStatusEnumMappings()
      this.$message.success('已恢复默认工作状态枚举')
    },
    buildWorkStatusEnumPayload() {
      const mappings = (this.workStatusEnumMappings || [])
        .map(item => ({
          deviceCategory: (item && item.deviceCategory) || '',
          matchField: (item && item.matchField) || '',
          enumCode: (item && item.enumCode) || '',
          enumName: (item && item.enumName) || '',
          enumDesc: (item && item.enumDesc) || '',
          dataEnumCode: (item && item.dataEnumCode) || ''
        }))
        .filter(item => (item.deviceCategory && item.matchField) && (item.enumCode || item.enumName || item.dataEnumCode))
      return {
        siteId: this.siteId,
        mappings
      }
    },
    handleDeleteItem(index, list) {
      if (!Array.isArray(list) || index < 0 || index >= list.length) {
        return
      }
      const currentRow = list[index]
      if (currentRow && currentRow.deviceId) {
        currentRow.point = ''
        currentRow.fixedValue = ''
        currentRow.useFixedDisplay = 0
        this.$message.success('已清空该设备映射，系统将自动保存')
        return
      }
      this.$confirm('是否确认删除该条明细记录？删除后需点击“保存”才会生效。', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const fieldCode = list[index] && list[index].field
        if (fieldCode && !this.deletedFieldCodes.includes(fieldCode)) {
          this.deletedFieldCodes.push(fieldCode)
        }
        list.splice(index, 1)
        this.$message.success('已删除该条明细记录，系统将自动保存')
      }).catch(() => {})
    },
    buildSavePayload() {
      const mappings = this.getAllMappingRows()
        .filter(item => item.field)
        .map(item => ({
          fieldCode: item.field,
          deviceId: item.deviceId || '',
          dataPoint: item.point || '',
          fixedDataPoint: item.fixedValue || '',
          useFixedDisplay: item.useFixedDisplay === 1 ? 1 : 0
        }))
      return {
        siteId: this.siteId,
        mappings,
        deletedFieldCodes: this.deletedFieldCodes
      }
    },
    buildPointPayloadSignature() {
      if (!this.siteId) {
        return ''
      }
      return JSON.stringify(this.buildSavePayload())
    },
    buildEnumPayloadSignature() {
      if (!this.siteId) {
        return ''
      }
      return JSON.stringify(this.buildWorkStatusEnumPayload())
    },
    scheduleAutoSave() {
      if (this.suppressAutoSave || !this.siteId) {
        return
      }
      const pointChanged = this.buildPointPayloadSignature() !== this.lastSavedPointSignature
      const enumChanged = this.buildEnumPayloadSignature() !== this.lastSavedEnumSignature
      if (!pointChanged && !enumChanged) {
        return
      }
      this.saveStatusText = '有未保存修改...'
      if (this.autoSaveTimer) {
        clearTimeout(this.autoSaveTimer)
      }
      this.autoSaveTimer = setTimeout(() => {
        this.persistMappings()
      }, this.autoSaveDelay)
    },
    async persistMappings() {
      if (!this.siteId) {
        return
      }
      if (this.isSaving) {
        return
      }
      const pointSignature = this.buildPointPayloadSignature()
      const enumSignature = this.buildEnumPayloadSignature()
      const pointChanged = !!pointSignature && pointSignature !== this.lastSavedPointSignature
      const enumChanged = !!enumSignature && enumSignature !== this.lastSavedEnumSignature
      if (!pointChanged && !enumChanged) {
        return
      }
      this.isSaving = true
      let pointError = null
      let enumError = null
      try {
        if (pointChanged) {
          try {
            await saveSingleMonitorProjectPointMapping(this.buildSavePayload())
            this.lastSavedPointSignature = pointSignature
          } catch (error) {
            pointError = error
          }
        }
        if (enumChanged) {
          try {
            await saveSingleMonitorWorkStatusEnumMappings(this.buildWorkStatusEnumPayload())
            this.lastSavedEnumSignature = enumSignature
          } catch (error) {
            enumError = error
          }
        }
        if (!pointError && !enumError) {
          this.saveStatusText = '已自动保存'
          return
        }
        if (pointError && !enumError) {
          this.saveStatusText = '状态映射已保存，点位映射保存失败'
          return
        }
        if (!pointError && enumError) {
          this.saveStatusText = '点位映射已保存，状态映射保存失败'
          return
        }
        this.saveStatusText = '自动保存失败，请稍后重试'
      } finally {
        this.isSaving = false
      }
    }
  },
  beforeDestroy() {
    if (this.autoSaveTimer) {
      clearTimeout(this.autoSaveTimer)
    }
    if (this.pointSelectorQueryTimer) {
      clearTimeout(this.pointSelectorQueryTimer)
      this.pointSelectorQueryTimer = null
    }
  },
  async mounted() {
    this.siteId = this.$route.query.siteId || ''
    this.siteName = this.$route.query.siteName || this.siteId
    await this.initMenuStructure()
  }
}
</script>

<style scoped lang="scss">
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.title-block {
  display: flex;
  flex-direction: column;
}

.page-title {
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}

.page-desc {
  margin-top: 6px;
  font-size: 13px;
  color: #909399;
  display: flex;
  align-items: center;
}

.site-label {
  margin-right: 8px;
}

.enum-mapping-card {
  margin-top: 12px;
}

.enum-mapping-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 14px;
  font-weight: 600;
}

.enum-scope-group {
  margin-bottom: 12px;
}

.enum-mapping-tip {
  margin-top: 8px;
  font-size: 12px;
  color: #909399;
}

.short-input {
  width: 220px;
}

.child-menu-tabs {
  margin-top: 8px;
}

.mode-switch-row {
  margin-top: 12px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  flex-wrap: wrap;
}

.save-status-text {
  font-size: 12px;
  color: #909399;
  min-width: 120px;
}

.quick-filter-input {
  width: 280px;
}

.filter-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.filter-actions:not(.single-battery-actions-visible) > .el-button:nth-of-type(3),
.filter-actions:not(.single-battery-actions-visible) > .el-button:nth-of-type(4) {
  display: none;
}

.import-result-title {
  margin-bottom: 10px;
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}

.quick-wrapper {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

.quick-card {
  border: 1px solid #ebeef5;
  border-radius: 6px;
  padding: 10px 12px;
}

.quick-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.quick-title-block {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.quick-title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}

.quick-sub-title {
  font-size: 12px;
  color: #909399;
}

.quick-row {
  padding: 8px 0;
}

.quick-row + .quick-row {
  border-top: 1px dashed #ebeef5;
}

.quick-device-name {
  width: auto;
  font-size: 13px;
  color: #606266;
  margin-bottom: 6px;
}

.quick-input {
  width: 100%;
  margin-bottom: 8px;
}

.section-wrapper {
  margin-top: 2px;
}

.section-collapse {
  border-top: 0;
  border-bottom: 0;
}

.section-title-row {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}

.section-badge {
  font-size: 12px;
  font-weight: 400;
  color: #909399;
  margin-right: 8px;
}

.section-field-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  padding-bottom: 8px;
}

.section-field-card {
  border: 1px solid #ebeef5;
  border-radius: 6px;
  padding: 10px 12px;
}

@media (min-width: 2200px) {
  .quick-wrapper {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  .section-field-list {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

@media (max-width: 1680px) {
  .quick-wrapper {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .section-field-list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 1280px) {
  .quick-wrapper {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .section-field-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
</style>
