<template>
  <div class="plan-detail page-container">
    <!-- Page Header -->
    <PageHeader :title="plan.name || '方案详情'" :subtitle="subtitleText">
      <template #actions>
        <el-button @click="goBack">
          <el-icon><ArrowLeft /></el-icon> 返回
        </el-button>
        <el-button v-if="!isRunning" type="primary" @click="startSimulation" :loading="starting || preflightLoading">
          <el-icon><VideoPlay /></el-icon> 启动仿真
        </el-button>
        <el-button v-else type="danger" @click="stopSimulation" :loading="stopping">
          <el-icon><VideoPause /></el-icon> 停止仿真
        </el-button>
        <el-button @click="showUpload = true">
          <el-icon><Upload /></el-icon> 上传结果
        </el-button>
      </template>
    </PageHeader>

    <!-- Running Progress Banner -->
    <div v-if="isRunning" class="progress-banner">
      <div class="progress-info">
        <span class="status-dot running" />
        <span class="progress-title">仿真运行中</span>
        <span class="progress-detail">
          {{ activeTask?.completed_points || 0 }} / {{ activeTask?.total_points || 0 }} 点
          · {{ activeTask?.progress || 0 }}%
          · 预计剩余 {{ estimatedRemaining }}
        </span>
      </div>
      <el-progress
        :percentage="activeTask?.progress || 0"
        :stroke-width="6"
        class="progress-bar"
      />
    </div>

    <!-- Main Tabs -->
    <el-tabs v-model="activeTab" class="plan-tabs">
      <!-- ========== Tab 1: Overview ========== -->
      <el-tab-pane label="概览" name="overview">
        <!-- Stat Cards -->
        <el-row :gutter="16" class="stat-row">
          <el-col :span="6">
            <StatCard label="扫描变量数" :value="scanVariables.length" unit="个" />
          </el-col>
          <el-col :span="6">
            <StatCard label="固定参数" :value="fixedParams.length" unit="项" />
          </el-col>
          <el-col :span="6">
            <StatCard label="预计数据点" :value="estimatedPoints" unit="点" />
          </el-col>
          <el-col :span="6">
            <StatCard
              label="预计耗时"
              :value="estimatedTimeMin"
              unit="分钟"
              :valueColor="estimatedTimeMin > 120 ? 'var(--color-warning)' : ''"
            />
            <div class="est-basis">{{ estimatedTimeBasis }}</div>
          </el-col>
        </el-row>

        <!-- AI Design Rationale -->
        <SectionCard title="AI 设计思路" v-if="plan.ai_reasoning">
          <div class="ai-reasoning">{{ plan.ai_reasoning }}</div>
        </SectionCard>

        <!-- Acceptance Criteria -->
        <SectionCard title="验收标准">
          <div v-if="acceptanceItems.length" class="criteria-list">
            <div v-for="(c, idx) in acceptanceItems" :key="idx" class="criteria-item">
              <el-icon class="criteria-icon"><CircleCheck /></el-icon>
              <span class="criteria-text">{{ c.text }}</span>
              <el-tag size="small" effect="plain" :type="c.origin === 'AI 设定' ? 'primary' : 'warning'" class="criteria-origin">{{ c.origin }}</el-tag>
            </div>
          </div>
          <el-empty v-else description="暂无验收标准，可在边界条件中设置目标转矩/效率/损耗等" :image-size="60" />
        </SectionCard>

        <!-- Latest Results Summary -->
        <SectionCard
          v-if="results.length"
          title="最新结果摘要"
          :subtitle="`共 ${results.length} 个数据点`"
        >
          <template #actions>
            <el-button size="small" text type="primary" @click="activeTab = 'results'">
              查看全部 <el-icon><ArrowRight /></el-icon>
            </el-button>
          </template>
          <el-table :data="results.slice(0, 5)" size="small" border>
            <el-table-column label="#" type="index" width="50" />
            <el-table-column v-for="col in coreResultColumns" :key="col.key" :prop="col.key" :label="col.label" min-width="110" align="right">
              <template #default="{ row }">
                <el-tooltip
                  v-if="col.key === 'status' && isFailed(row) && row.error_message"
                  :content="row.error_message"
                  placement="top"
                  :show-after="100"
                  raw-content
                >
                  <span class="text-danger status-failed-cell">{{ formatCell(row[col.key]) }}</span>
                </el-tooltip>
                <span v-else :class="{ 'text-danger': isFailed(row) }">{{ formatCell(row[col.key]) }}</span>
              </template>
            </el-table-column>
          </el-table>
        </SectionCard>

        <!-- Quick Actions -->
        <SectionCard title="快捷操作">
          <div class="quick-actions">
            <div class="quick-action-card" @click="activeTab = 'parameters'">
              <el-icon :size="24" color="var(--color-primary)"><Setting /></el-icon>
              <div class="qa-title">编辑参数</div>
              <div class="qa-desc">扫描变量、边界条件、固定参数</div>
            </div>
            <div class="quick-action-card" @click="activeTab = 'results'">
              <el-icon :size="24" color="var(--color-success)"><DataAnalysis /></el-icon>
              <div class="qa-title">查看结果</div>
              <div class="qa-desc">{{ results.length }} 个数据点</div>
            </div>
            <div class="quick-action-card" @click="activeTab = 'ai'">
              <el-icon :size="24" color="#7c3aed"><MagicStick /></el-icon>
              <div class="qa-title">AI 闭环</div>
              <div class="qa-desc">分析、迭代、经验提取</div>
            </div>
            <div class="quick-action-card" @click="downloadResults">
              <el-icon :size="24" color="var(--color-warning)"><Download /></el-icon>
              <div class="qa-title">下载结果</div>
              <div class="qa-desc">CSV / JSON 格式</div>
            </div>
          </div>
        </SectionCard>
      </el-tab-pane>

      <!-- ========== Tab 2: Parameters ========== -->
      <el-tab-pane label="方案参数" name="parameters">
        <!-- Scan Variables -->
        <SectionCard title="扫描变量">
          <template #actions>
            <el-button v-if="scanVarsDirty" size="small" type="warning" :loading="savingVars" @click="saveScanVariables">
              保存修改
            </el-button>
            <el-button size="small" type="primary" @click="showAddVar = true">
              <el-icon><Plus /></el-icon> 添加变量
            </el-button>
          </template>
          <el-table :data="scanVariables" size="small" border>
            <el-table-column prop="name" label="变量名" min-width="140" />
            <el-table-column prop="name_cn" label="中文名" min-width="120" />
            <el-table-column label="取值" min-width="200">
              <template #default="{ row }">
                <span v-if="Array.isArray(row.values) && row.values.length" class="var-values">{{ row.values.join(' / ') }}</span>
                <span v-else>{{ row.min_value }} ~ {{ row.max_value }} (步长 {{ row.step }})</span>
              </template>
            </el-table-column>
            <el-table-column prop="unit" label="单位" width="80" align="center" />
            <el-table-column label="点数" width="80" align="center">
              <template #default="{ row }">{{ calcPoints(row) }}</template>
            </el-table-column>
            <el-table-column label="操作" width="100" fixed="right">
              <template #default="{ row, $index }">
                <el-button size="small" link type="danger" @click="removeVariable($index)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-empty v-if="!scanVariables.length" description="暂无扫描变量，点击添加" :image-size="60" />
        </SectionCard>

        <!-- Boundary Conditions -->
        <SectionCard title="边界条件">
          <template #actions>
            <template v-if="!bcEditing">
              <el-button size="small" @click="startBcEdit">
                <el-icon><Edit /></el-icon> 编辑
              </el-button>
            </template>
            <template v-else>
              <el-button size="small" @click="bcEditing = false">取消</el-button>
              <el-button size="small" type="primary" :loading="bcSaving" @click="saveBcEdit">保存</el-button>
            </template>
          </template>
          <el-descriptions :column="3" border size="small">
            <el-descriptions-item
              v-for="bc in boundaryConditions"
              :key="bc.key"
              :label="bc.label"
            >
              <template #label>
                <span>{{ bc.label }}</span>
                <el-tag
                  v-if="bc.source === 'user'"
                  size="small" effect="plain" type="success" class="bc-source-tag"
                >用户指定</el-tag>
                <el-tag
                  v-else-if="bc.source === 'ai'"
                  size="small" effect="plain" type="primary" class="bc-source-tag"
                >AI 补充</el-tag>
                <el-tag
                  v-else-if="bc.source === 'edited'"
                  size="small" effect="plain" type="warning" class="bc-source-tag"
                >已修改</el-tag>
              </template>
              <template v-if="bcEditing">
                <el-select
                  v-if="bc.type === 'enum'"
                  v-model="bcEditForm[bc.key]"
                  clearable
                  placeholder="未设置"
                  size="small"
                  style="width: 100%"
                >
                  <el-option v-for="opt in bc.options" :key="opt" :label="opt" :value="opt" />
                </el-select>
                <el-input-number
                  v-else
                  v-model="bcEditForm[bc.key]"
                  :precision="bc.type === 'int' ? 0 : 2"
                  :placeholder="'未设置'"
                  controls-position="right"
                  size="small"
                  style="width: 100%"
                />
              </template>
              <template v-else>
                {{ bc.value !== undefined && bc.value !== null && bc.value !== '' ? bc.value + (bc.unit ? ' ' + bc.unit : '') : '未设置' }}
              </template>
            </el-descriptions-item>
          </el-descriptions>
        </SectionCard>

        <!-- Fixed Parameters (collapsible) -->
        <SectionCard title="固定参数">
          <template #actions>
            <span class="fixed-summary">
              {{ modifiedFixedParams.length }} 项已修改 / 共 {{ fixedParams.length }} 项
            </span>
            <el-button size="small" @click="showAllFixed = !showAllFixed">
              {{ showAllFixed ? '只看修改项' : '全部展开' }}
            </el-button>
            <template v-if="!fixedEditing">
              <el-button size="small" @click="startFixedEdit">
                <el-icon><Edit /></el-icon> 编辑
              </el-button>
            </template>
            <template v-else>
              <el-button size="small" @click="fixedEditing = false">取消</el-button>
              <el-button size="small" type="primary" :loading="fixedSaving" @click="saveFixedParams">保存</el-button>
            </template>
          </template>

          <!-- Modified params always visible -->
          <div v-if="modifiedFixedParams.length" class="fixed-modified">
            <div class="fixed-group-title">
              <el-icon><Star /></el-icon> 已修改参数
            </div>
            <el-table :data="modifiedFixedParams" size="small" border>
              <el-table-column prop="name_cn" label="参数" min-width="140" />
              <el-table-column prop="name" label="Motor-CAD 变量" min-width="160">
                <template #default="{ row }">
                  <code class="var-code">{{ row.motorcad_var || row.name }}</code>
                </template>
              </el-table-column>
              <el-table-column label="当前值" width="120" align="right">
                <template #default="{ row }">
                  <span class="modified-value">{{ row.value }}</span>
                </template>
              </el-table-column>
              <el-table-column label="默认值" width="100" align="center">
                <template #default="{ row }">
                  <span class="text-muted">{{ getDefaultValue(row.name) }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="unit" label="单位" width="70" align="center" />
              <el-table-column prop="category_cn" label="分类" width="100" align="center" />
            </el-table>
          </div>

          <!-- All params (collapsible by category) -->
          <div v-if="showAllFixed" class="fixed-all">
            <div v-for="cat in fixedCategories" :key="cat" class="fixed-category">
              <div class="fixed-category-title" @click="toggleCategory(cat)">
                <el-icon class="cat-arrow">
                  <ArrowDown v-if="expandedCategories[cat] !== false" />
                  <ArrowRight v-else />
                </el-icon>
                {{ cat }}
                <span class="cat-count">({{ fixedParamsByCategory(cat).length }})</span>
              </div>
              <el-table
                v-if="expandedCategories[cat] !== false"
                :data="fixedParamsByCategory(cat)"
                size="small"
                border
              >
                <el-table-column prop="name_cn" label="参数" min-width="130" />
                <el-table-column prop="name" label="变量名" min-width="150">
                  <template #default="{ row }">
                    <code class="var-code">{{ row.motorcad_var || row.name }}</code>
                    <el-tag v-if="row.description_cn?.includes('需确认')" size="small" type="warning" effect="plain">需确认</el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="值" width="140" align="right">
                  <template #default="{ row }">
                    <template v-if="fixedEditing">
                      <el-select
                        v-if="typeof row.value === 'boolean' || row.value === 'True' || row.value === 'False'"
                        v-model="fixedEditValues[row.name]"
                        size="small"
                        style="width: 110px"
                      >
                        <el-option label="True" value="True" />
                        <el-option label="False" value="False" />
                      </el-select>
                      <el-input-number
                        v-else-if="!isNaN(Number(row.value)) && String(row.value).trim() !== ''"
                        v-model="fixedEditValues[row.name]"
                        :precision="Number.isInteger(Number(row.value)) ? 0 : 4"
                        controls-position="right"
                        size="small"
                        style="width: 130px"
                      />
                      <el-input v-else v-model="fixedEditValues[row.name]" size="small" style="width: 130px" />
                    </template>
                    <span v-else :class="{ 'modified-value': isFixedModified(row) }">{{ row.value }}</span>
                  </template>
                </el-table-column>
                <el-table-column prop="unit" label="单位" width="70" align="center" />
                <el-table-column prop="description_cn" label="说明" min-width="140" show-overflow-tooltip />
              </el-table>
            </div>
          </div>

          <el-empty
            v-if="!modifiedFixedParams.length && !showAllFixed"
            description="所有参数均为默认值"
            :image-size="60"
          />
        </SectionCard>
      </el-tab-pane>

      <!-- ========== Tab 3: Results ========== -->
      <el-tab-pane :label="`仿真结果 (${results.length})`" name="results">
        <SectionCard title="仿真结果" no-padding>
          <template #actions>
            <el-button size="small" @click="openColumnSettings" :disabled="!results.length">
              <el-icon><Setting /></el-icon> 列设置
            </el-button>
            <el-button size="small" @click="downloadResults" :disabled="!results.length">
              <el-icon><Download /></el-icon> 下载 CSV
            </el-button>
            <el-button size="small" type="primary" plain @click="exportExcel" :disabled="!results.length">
              <el-icon><Download /></el-icon> 导出 Excel
            </el-button>
          </template>
          <el-table :data="results" size="small" border max-height="600">
            <el-table-column label="#" type="index" width="50" fixed="left" />
            <el-table-column
              v-for="col in displayColumns"
              :key="col.key"
              :prop="col.key"
              :label="col.label"
              :min-width="col.width || 110"
              align="right"
            >
              <template #default="{ row }">
                <el-tooltip
                  v-if="col.key === 'status' && isFailed(row) && row.error_message"
                  :content="row.error_message"
                  placement="top"
                  :show-after="100"
                  raw-content
                >
                  <span class="text-danger status-failed-cell">{{ formatCell(row[col.key]) }}</span>
                </el-tooltip>
                <span v-else :class="{ 'text-danger': isFailed(row) }">{{ formatCell(row[col.key]) }}</span>
              </template>
            </el-table-column>
          </el-table>
          <el-empty v-if="!results.length" description="暂无仿真结果" :image-size="80" />
        </SectionCard>
      </el-tab-pane>

      <!-- ========== Tab 4: AI Loop ========== -->
      <el-tab-pane label="AI 闭环" name="ai">
        <!-- Step indicator -->
        <el-steps :active="aiStep" finish-status="success" align-center class="ai-steps">
          <el-step title="AI 分析" description="分析当前结果" />
          <el-step title="生成迭代" description="生成下一轮方案" />
          <el-step title="提取经验" description="沉淀到经验库" />
        </el-steps>

        <!-- Step 1: AI Analysis -->
        <SectionCard title="1. AI 结果分析">
          <template #actions>
            <el-button
              size="small"
              type="primary"
              :loading="analyzing"
              @click="runAIAnalysis"
              :disabled="!results.length"
            >
              <el-icon><MagicStick /></el-icon> 运行分析
            </el-button>
          </template>
          <div v-if="aiAnalysis" class="ai-analysis-content">
            <div class="ai-section">
              <div class="ai-section-title">关键发现</div>
              <div class="ai-section-body">{{ aiAnalysis.findings || '暂无' }}</div>
            </div>
            <div class="ai-section">
              <div class="ai-section-title">优化建议</div>
              <div class="ai-section-body">{{ aiAnalysis.suggestions || '暂无' }}</div>
            </div>
          </div>
          <el-empty v-else description="点击运行 AI 分析当前仿真结果" :image-size="60" />
        </SectionCard>

        <!-- Step 2: Generate Iteration -->
        <SectionCard title="2. 生成下一轮迭代方案">
          <template #actions>
            <el-button
              size="small"
              type="primary"
              :loading="generating"
              @click="generateIteration"
              :disabled="!aiAnalysis"
            >
              <el-icon><RefreshRight /></el-icon> 生成迭代方案
            </el-button>
          </template>
          <div v-if="iterationPlan" class="iteration-content">
            <el-alert
              :title="`迭代方案已生成：${iterationPlan.name || '新方案'}`"
              type="success"
              :closable="false"
              show-icon
            />
            <div class="mt-3">
              <el-button type="primary" @click="viewIteration">查看迭代方案</el-button>
            </div>
          </div>
          <el-empty v-else description="先运行 AI 分析，再生成迭代方案" :image-size="60" />
        </SectionCard>

        <!-- Step 3: Extract Experience -->
        <SectionCard title="3. 提取经验入库">
          <template #actions>
            <el-button
              size="small"
              type="primary"
              :loading="extracting"
              @click="extractExperience"
              :disabled="!results.length"
            >
              <el-icon><Collection /></el-icon> 提取经验
            </el-button>
          </template>
          <div v-if="experienceExtracted" class="experience-content">
            <el-alert title="经验已成功提取到经验库" type="success" :closable="false" show-icon />
            <div class="mt-3">
              <el-button @click="$router.push('/experience')">查看经验库</el-button>
            </div>
          </div>
          <el-empty v-else description="将本次仿真的关键参数和结论提取为经验" :image-size="60" />
        </SectionCard>
      </el-tab-pane>
    </el-tabs>

    <!-- Add Variable Dialog -->
    <el-dialog v-model="showAddVar" title="添加扫描变量" width="500px">
      <el-form :model="newVar" label-width="100px">
        <el-form-item label="变量名">
          <el-select v-model="newVar.name" filterable placeholder="选择 Motor-CAD 变量" @change="onVarSelect">
            <el-option
              v-for="v in availableScanParams"
              :key="v.name"
              :label="`${v.name_cn} (${v.name}) -> ${v.motorcad_var}`"
              :value="v.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="最小值">
          <el-input-number v-model="newVar.min_value" :precision="4" controls-position="right" style="width: 100%" />
        </el-form-item>
        <el-form-item label="最大值">
          <el-input-number v-model="newVar.max_value" :precision="4" controls-position="right" style="width: 100%" />
        </el-form-item>
        <el-form-item label="步长">
          <el-input-number v-model="newVar.step" :precision="4" :min="0.0001" controls-position="right" style="width: 100%" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showAddVar = false">取消</el-button>
        <el-button type="primary" @click="confirmAddVar">确认添加</el-button>
      </template>
    </el-dialog>

    <!-- Upload Results Dialog -->
    <el-dialog v-model="showUpload" title="上传仿真结果" width="500px">
      <el-upload
        drag
        action="#"
        :auto-upload="false"
        :on-change="onFileChange"
        accept=".csv,.json"
      >
        <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
        <div class="el-upload__text">拖拽文件到此处，或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip">支持 CSV / JSON 格式的仿真结果文件</div>
        </template>
      </el-upload>
      <template #footer>
        <el-button @click="showUpload = false">取消</el-button>
        <el-button type="primary" :loading="uploading" @click="confirmUpload" :disabled="!uploadFile">
          上传
        </el-button>
      </template>
    </el-dialog>

    <!-- Pre-flight Checklist Dialog -->
    <el-dialog v-model="showPreflight" title="仿真前检查" width="560px" :close-on-click-modal="false">
      <div v-if="preflight" class="preflight-body">
        <div v-for="c in preflight.checks" :key="c.key" class="preflight-item" :class="`is-${c.status}`">
          <el-icon class="preflight-icon">
            <CircleCheckFilled v-if="c.status === 'pass'" />
            <WarningFilled v-else-if="c.status === 'warn'" />
            <CircleCloseFilled v-else />
          </el-icon>
          <div class="preflight-content">
            <div class="preflight-label">{{ PREFLIGHT_LABELS[c.key] || c.key }}</div>
            <div class="preflight-message">{{ preflightMessage(c) }}</div>
            <el-button
              v-if="c.key === 'model_path' && c.status === 'fail' && c.fixable"
              size="small"
              type="primary"
              class="preflight-fix"
              :loading="applyingDefaultModel"
              @click="applyDefaultModel(c.default_model)"
            >
              使用拓扑默认模型（{{ c.default_model }}）
            </el-button>
          </div>
        </div>
        <el-divider />
        <div class="thermal-mode-row">
          <span class="thermal-mode-label">热仿真模式</span>
          <el-radio-group v-model="thermalMode" size="small">
            <el-radio value="off">仅电磁（最快）</el-radio>
            <el-radio value="steady">电磁+稳态热（默认）</el-radio>
            <el-radio value="coupled">磁热耦合（精算）</el-radio>
          </el-radio-group>
        </div>
        <el-alert v-if="!preflight.ok" type="error" :closable="false" style="margin-top: 12px">
          存在未通过的检查项，请先处理后再启动仿真。
        </el-alert>
      </div>
      <template #footer>
        <el-button @click="showPreflight = false">取消</el-button>
        <el-button type="primary" :disabled="!preflight?.ok" :loading="starting" @click="confirmStartSimulation">
          确认启动
        </el-button>
      </template>
    </el-dialog>

    <!-- Result Column Settings Dialog -->
    <el-dialog v-model="showColumnSettings" title="结果列设置" width="480px">
      <div class="column-settings-tip">勾选要在结果表中显示的列（默认显示核心指标与扫描变量）</div>
      <el-checkbox-group v-model="visibleColumnKeys" class="column-settings-group">
        <el-checkbox v-for="col in allResultColumns" :key="col.key" :label="col.key" class="column-settings-item">
          {{ col.label }}
        </el-checkbox>
      </el-checkbox-group>
      <template #footer>
        <el-button @click="resetColumns">恢复默认</el-button>
        <el-button type="primary" @click="confirmColumnSettings">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  ArrowLeft, VideoPlay, VideoPause, Upload, Plus, Edit,
  Download, DataAnalysis, MagicStick, Setting, CircleCheck,
  Star, ArrowDown, ArrowRight, RefreshRight, Collection, UploadFilled,
  CircleCheckFilled, WarningFilled, CircleCloseFilled,
} from '@element-plus/icons-vue'
import api from '@/api'
import StatCard from '@/components/StatCard.vue'
import SectionCard from '@/components/SectionCard.vue'
import PageHeader from '@/components/PageHeader.vue'

const route = useRoute()
const router = useRouter()
const planId = computed(() => Number(route.params.id))

// State
const plan = reactive<any>({})
const results = ref<any[]>([])
const activeTask = ref<any>(null)
const activeTab = ref('overview')
const isRunning = ref(false)
const starting = ref(false)
const stopping = ref(false)
const analyzing = ref(false)
const generating = ref(false)
const extracting = ref(false)
const uploading = ref(false)
const uploadFile = ref<File | null>(null)
const showAddVar = ref(false)
const scanVarsDirty = ref(false)
const savingVars = ref(false)
const showUpload = ref(false)
const showAllFixed = ref(true)  // default fully expanded (user requirement 2026-09-03)
const showPreflight = ref(false)
const preflight = ref<any>(null)
const preflightLoading = ref(false)
const applyingDefaultModel = ref(false)
const solveTimeStats = ref<any>(null)
const aiStep = ref(0)
const aiAnalysis = ref<any>(null)
const iterationPlan = ref<any>(null)
const experienceExtracted = ref(false)

const newVar = reactive({
  name: '',
  name_cn: '',
  min_value: 0,
  max_value: 1,
  step: 0.1,
  unit: '',
})

const expandedCategories = reactive<Record<string, boolean>>({})

// Scan parameter catalog (frontend copy; B3 will unify with backend)
const SCAN_PARAM_CN: Record<string, string> = {
  Airgap: '气隙长度',
  Magnet_Length: '磁钢轴向长度',
  Magnet_Thickness: '磁钢径向厚度',
  'Magnet_Arc_[ED]': '磁钢极弧角',
  RMSCurrent: '额定电流',
  Shaft_Speed: '额定转速',
  Slot_Depth: '槽深',
  Slot_Width: '槽宽',
  Tooth_Width: '齿宽',
  Turns_per_Coil: '每线圈匝数',
  Stator_Outer_Diameter: '定子外径',
  Stator_Inner_Diameter: '定子内径',
  Stator_Axial_Length: '定子轴向长度',
  Wire_Diameter: '线径',
}

const categoryCnMap: Record<string, string> = {
  Geometry: '几何尺寸',
  Stator: '定子参数',
  Rotor: '转子参数',
  Performance: '性能规格',
  Winding: '绕组参数',
  Material: '材料属性',
  Thermal: '热与冷却',
  Simulation: '仿真设置',
  System: '仿真设置',
  Mechanical: '其他',
  Electrical: '性能规格',
  General: '其他',
}

// BC field catalog loaded from the backend single-source (P1-1). The plan
// boundary display renders from this canonical catalog instead of a local
// hard-coded template that had drifted to a third key naming.
const bcFieldCatalog = ref<any[]>([])

// Fixed parameter default template (36 params)
const ALL_FIXED_PARAM_TEMPLATE = [
  { name: 'Outer_Rotor_Diameter', motorcad_var: null, name_cn: '转子外径', unit: 'mm', value: 200.0, category: 'Geometry', description_cn: '转子背铁外径' },
  { name: 'Inner_Rotor_Diameter', motorcad_var: null, name_cn: '转子内径', unit: 'mm', value: 120.0, category: 'Geometry', description_cn: '转子背铁内径' },
  { name: 'Stator_Outer_Diameter', motorcad_var: null, name_cn: '定子外径', unit: 'mm', value: 198.0, category: 'Geometry', description_cn: '定子冲片外径' },
  { name: 'Stator_Inner_Diameter', motorcad_var: null, name_cn: '定子内径', unit: 'mm', value: 122.0, category: 'Geometry', description_cn: '定子冲片内径' },
  { name: 'Airgap', motorcad_var: 'Airgap', name_cn: '气隙长度', unit: 'mm', value: 1.0, category: 'Geometry', description_cn: '定转子之间气隙' },
  { name: 'Stator_Yoke_Thickness', motorcad_var: null, name_cn: '定子轭厚', unit: 'mm', value: 10.0, category: 'Stator', description_cn: '定子轭部径向厚度' },
  { name: 'Slot_Depth', motorcad_var: 'Slot_Depth', name_cn: '槽深', unit: 'mm', value: 7.0, category: 'Stator', description_cn: '定子槽深度' },
  { name: 'Slot_Width', motorcad_var: 'Slot_Width', name_cn: '槽宽', unit: 'mm', value: 8.5, category: 'Stator', description_cn: '定子槽口宽度' },
  { name: 'Tooth_Width', motorcad_var: 'Tooth_Width', name_cn: '齿宽', unit: 'mm', value: 7.0, category: 'Stator', description_cn: '定子齿宽度' },
  { name: 'Number_of_Slots', motorcad_var: 'Slot_Number', name_cn: '槽数', unit: '', value: 12, category: 'Stator', description_cn: '定子总槽数' },
  { name: 'Rotor_Back_Iron_Thickness', motorcad_var: null, name_cn: '转子背铁厚', unit: 'mm', value: 8.0, category: 'Rotor', description_cn: '转子背铁径向厚度' },
  { name: 'Magnet_Length', motorcad_var: 'Magnet_Length', name_cn: '磁钢轴向长度', unit: 'mm', value: 3.0, category: 'Rotor', description_cn: '永磁体轴向厚度' },
  { name: 'Magnet_Thickness', motorcad_var: 'Magnet_Thickness', name_cn: '磁钢径向厚度', unit: 'mm', value: 13.0, category: 'Rotor', description_cn: '磁钢环径向厚度' },
  { name: 'Magnet_Arc_[ED]', motorcad_var: 'Magnet_Arc_[ED]', name_cn: '磁钢极弧角', unit: 'deg', value: 121.0, category: 'Rotor', description_cn: '磁钢极弧角度(电角度)' },
  { name: 'Number_of_Poles', motorcad_var: 'Pole_Number', name_cn: '极数', unit: '', value: 10, category: 'Rotor', description_cn: '电机总极数' },
  { name: 'Current_Advance_Angle', motorcad_var: null, name_cn: '电流超前角', unit: 'deg', value: 0.0, category: 'Performance', description_cn: '弱磁控制电流超前角' },
  { name: 'DC_Link_Voltage', motorcad_var: 'DCBusVoltage', name_cn: '母线电压', unit: 'V', value: 13.5, category: 'Performance', description_cn: '直流母线电压' },
  { name: 'RMSCurrent', motorcad_var: 'RMSCurrent', name_cn: '额定电流', unit: 'A', value: 21.0, category: 'Performance', description_cn: '额定相电流(RMS)' },
  { name: 'Shaft_Speed', motorcad_var: 'Shaft_Speed', name_cn: '额定转速', unit: 'rpm', value: 5000.0, category: 'Performance', description_cn: '电机额定转速' },
  { name: 'Max_Speed', motorcad_var: 'WindageGraph_MaxSpeed', name_cn: '最高转速', unit: 'rpm', value: 7500.0, category: 'Performance', description_cn: '电机最高转速（变量名需确认）' },
  { name: 'Turns_per_Coil', motorcad_var: 'ConductorsPerSlot', name_cn: '每线圈匝数', unit: '', value: 20, category: 'Winding', description_cn: '每个线圈的匝数' },
  { name: 'Parallel_Paths', motorcad_var: 'ParallelPaths', name_cn: '并联支路数', unit: '', value: 1, category: 'Winding', description_cn: '绕组并联支路数' },
  { name: 'Copper_Fill_Factor', motorcad_var: 'Slot_Fill', name_cn: '槽满率', unit: '', value: 0.45, category: 'Winding', description_cn: '铜线面积与槽面积之比' },
  { name: 'Wire_Diameter', motorcad_var: 'Wire_Diameter', name_cn: '线径', unit: 'mm', value: 1.63, category: 'Winding', description_cn: '漆包线直径' },
  { name: 'Winding_Connection', motorcad_var: 'WindingConnection', name_cn: '绕组连接方式', unit: '', value: 'Star', category: 'Winding', description_cn: '绕组连接方式(星/三角)（变量名需确认）' },
  { name: 'Current_Density', motorcad_var: null, name_cn: '电流密度', unit: 'A/mm2', value: 6.0, category: 'Winding', description_cn: '绕组电流密度（变量名需确认）' },
  { name: 'Magnet_Material', motorcad_var: 'Material_Magnet', name_cn: '磁钢材料', unit: '', value: 'NdFeB_N42SH', category: 'Material', description_cn: '永磁体材料牌号' },
  { name: 'Steel_Grade', motorcad_var: 'Material_Stator_Lam_Yoke', name_cn: '硅钢片牌号', unit: '', value: 'M19_24G', category: 'Material', description_cn: '电工硅钢片牌号' },
  { name: 'Magnet_Temperature', motorcad_var: 'InitialMagnetTemperature', name_cn: '磁钢温度', unit: 'C', value: 40.0, category: 'Material', description_cn: '磁钢工作温度' },
  { name: 'Magnet_Remanence', motorcad_var: 'Magnet_Br_at_RefTemp', name_cn: '剩磁', unit: 'T', value: 1.31, category: 'Material', description_cn: '永磁体剩磁密度（变量名需确认）' },
  { name: 'Ambient_Temperature', motorcad_var: 'Ambient_Temperature', name_cn: '环境温度', unit: 'C', value: 40.0, category: 'Thermal', description_cn: '环境空气温度' },
  { name: 'Cooling_Method', motorcad_var: 'Cooling_Type', name_cn: '冷却方式', unit: '', value: 'Natural', category: 'Thermal', description_cn: '冷却方式(自然/强迫风冷/水冷)' },
  { name: 'Insulation_Class', motorcad_var: null, name_cn: '绝缘等级', unit: '', value: 'F', category: 'Thermal', description_cn: '绝缘等级(A/E/B/F/H)（变量名需确认）' },
  { name: 'TorquePointsPerCycle', motorcad_var: 'TorquePointsPerCycle', name_cn: '每周期转矩采样点', unit: '', value: 30, category: 'Simulation', description_cn: '每电周期转矩计算采样点数' },
  { name: 'CurrentDefinition', motorcad_var: null, name_cn: '电流定义方式', unit: '', value: 1.0, category: 'Simulation', description_cn: '1=有效值, 2=峰值电流定义' },
  { name: 'AirgapMeshPoints_mesh', motorcad_var: 'AirgapMeshPoints_mesh', name_cn: '气隙网格点数', unit: '', value: 600.0, category: 'Simulation', description_cn: '气隙网格点数(显式求解网格加密)' },
  { name: 'MessageDisplayState', motorcad_var: 'MessageDisplayState', name_cn: '消息显示', unit: '', value: 2.0, category: 'Simulation', description_cn: '脚本运行时抑制弹窗' },
]

// Computed
const subtitleText = computed(() => {
  const parts: string[] = []
  if (plan.topology) parts.push(plan.topology)
  if (plan.created_at) parts.push(`创建于 ${new Date(plan.created_at).toLocaleDateString('zh-CN')}`)
  return parts.join(' · ')
})

// Scan variables live under plan_data.variables in the plan payload (P0 fix:
// previously read the non-existent plan.scan_variables, so the overview stat
// cards and the parameters tab always showed 0/empty for every plan).
const scanVariables = computed(() => plan.plan_data?.variables || plan.scan_variables || [])

const fixedParams = computed(() => {
  const fps = plan.fixed_parameters || []
  // Backfill from template if missing
  if (fps.length === 0) {
    return ALL_FIXED_PARAM_TEMPLATE.map(p => ({ ...p, category_cn: categoryCnMap[p.category] || p.category }))
  }
  return fps.map((p: any) => ({
    ...p,
    category_cn: p.category_cn || categoryCnMap[p.category] || p.category,
    name_cn: p.name_cn || SCAN_PARAM_CN[p.name] || p.name,
  }))
})

// BC lives under plan_data (not top-level). Show ALL catalog fields (unset
// ones included) so the full 25-field boundary picture is visible; tag each
// with its source (user-specified / AI-supplemented / edited).
const bcMeta = computed(() => plan.plan_data?.bc_meta || {})

const boundaryConditions = computed(() => {
  const bc = plan.plan_data?.boundary_conditions || plan.boundary_conditions || {}
  return bcFieldCatalog.value.map(t => ({
    ...t,
    value: bc[t.key],
    source: bcMeta.value[t.key]?.source || (bc[t.key] != null && bc[t.key] !== '' ? 'user' : ''),
  }))
})

// Acceptance criteria: AI-provided criteria first, then derived from BC target
// fields so the overview always shows the plan's goals at a glance.
const ACCEPTANCE_FROM_BC: { key: string; metric: string; label: string; op: string; unit: string }[] = [
  { key: 'target_torque_nm', metric: 'tavg_nm', label: '平均转矩', op: '>=', unit: 'Nm' },
  { key: 'target_efficiency_pct', metric: 'efficiency_pct', label: '效率', op: '>=', unit: '%' },
  { key: 'max_losses_w', metric: 'total_losses_w', label: '总损耗', op: '<=', unit: 'W' },
  { key: 'target_ripple_pct', metric: 'ripple_pct', label: '转矩脉动', op: '<=', unit: '%' },
  { key: 'max_axial_force_n', metric: 'axial_force_n', label: '轴向力', op: '<=', unit: 'N' },
  { key: 'outer_diameter_mm', metric: 'outer_diameter', label: '外径', op: '<=', unit: 'mm' },
  { key: 'axial_length_mm', metric: 'axial_length', label: '轴向长度', op: '<=', unit: 'mm' },
]

const acceptanceItems = computed(() => {
  const ac = plan.plan_data?.acceptance_criteria || plan.acceptance_criteria
  const items: { text: string; origin: string }[] = []
  if (ac && Array.isArray(ac.hard_constraints)) {
    ac.hard_constraints.forEach((c: string) => items.push({ text: c, origin: 'AI 设定' }))
  } else if (typeof ac === 'string' && ac) {
    items.push({ text: ac, origin: 'AI 设定' })
  }
  const bc = plan.plan_data?.boundary_conditions || {}
  ACCEPTANCE_FROM_BC.forEach(m => {
    const v = bc[m.key]
    if (v !== undefined && v !== null && v !== '') {
      items.push({ text: `${m.label} ${m.op} ${v} ${m.unit}`.trim(), origin: '边界条件' })
    }
  })
  return items
})

const estimatedPoints = computed(() => {
  if (!scanVariables.value.length) return 0
  return scanVariables.value.reduce((acc: number, v: any) => acc * calcPoints(v), 1)
})

// Per-point duration calibrated from measured solve times (P2-1); falls back
// to the 2.5 min/point heuristic when no measurement exists yet.
const perPointMin = computed(() => {
  const avg = solveTimeStats.value?.avg_s
  return avg ? avg / 60 : 2.5
})

const estimatedTimeMin = computed(() => {
  return Math.round(estimatedPoints.value * perPointMin.value)
})

const estimatedTimeBasis = computed(() => {
  const s = solveTimeStats.value
  if (s && s.count) return `基于最近 ${s.count} 次实测（~${Math.round(s.avg_s)}s/点）`
  return '按经验值 2.5 分钟/点估算'
})

const estimatedRemaining = computed(() => {
  if (!activeTask.value || !activeTask.value.total_points) return '-'
  const remaining = activeTask.value.total_points - (activeTask.value.completed_points || 0)
  const mins = Math.round(remaining * perPointMin.value)
  if (mins < 60) return `${mins} 分钟`
  return `${(mins / 60).toFixed(1)} 小时`
})

const modifiedFixedParams = computed(() => {
  return fixedParams.value.filter((p: any) => {
    const def = ALL_FIXED_PARAM_TEMPLATE.find(t => t.name === p.name)
    if (!def) return true // unknown param, treat as modified
    return String(p.value) !== String(def.value)
  })
})

const fixedCategories = computed<string[]>(() => {
  const fps = fixedParams.value as any[]
  const cats = [...new Set(fps.map((p: any) => String(p.category_cn || p.category || '其他')))]
  return cats
})

// Topology-aware variable catalog fetched from backend
const variableCatalog = ref<any>(null)

async function loadVariableCatalog() {
  try {
    const topo = (plan.topology as string) || 'SSSR'
    const res: any = await api.get('/plans/variable-catalog', { params: { topology: topo } })
    variableCatalog.value = res
  } catch (e) {
    console.error('Failed to load variable catalog:', e)
  }
}

const availableScanParams = computed(() => {
  const usedNames = new Set(scanVariables.value.map((v: any) => v.name))
  // Prefer backend template (topology-aware with motorcad_var), fallback to local
  const template = variableCatalog.value?.template || ALL_FIXED_PARAM_TEMPLATE
  return template
    .filter((p: any) => !usedNames.has(p.name))
    .map((p: any) => ({
      name: p.name,
      name_cn: p.name_cn || p.name,
      unit: p.unit,
      motorcad_var: p.motorcad_var || p.name,
    }))
})

const coreResultColumns = [
  { key: 'tavg_nm', label: '平均转矩(Nm)', width: 120 },
  { key: 'ripple_pct', label: '转矩脉动(%)', width: 110 },
  { key: 'efficiency_pct', label: '效率(%)', width: 90 },
  { key: 'total_losses_w', label: '总损耗(W)', width: 110 },
  { key: 'status', label: '状态', width: 80 },
]

const allResultColumns = computed(() => {
  if (!results.value.length) return coreResultColumns
  const keys = Object.keys(results.value[0]) as string[]
  const labelMap: Record<string, string> = {
    point_id: '点ID', index: '序号', Airgap: '气隙(mm)', Magnet_Length: '磁钢长(mm)',
    Magnet_Thickness: '磁钢厚(mm)', 'Magnet_Arc_[ED]': '极弧角(deg)',
    RMSCurrent: '电流(A)', Shaft_Speed: '转速(rpm)',
    tavg_nm: '平均转矩(Nm)', ripple_pct: '转矩脉动(%)',
    average_torque_nm: '平均转矩(Nm)', torque_ripple_pct: '转矩脉动(%)',
    cogging_torque_nm: '齿槽转矩(Nm)', efficiency_pct: '效率(%)',
    total_losses_w: '总损耗(W)', copper_loss_w: '铜耗(W)',
    iron_loss_w: '铁耗(W)', magnet_loss_w: '磁钢耗(W)',
    max_flux_density_t: '最大磁密(T)', status: '状态',
    solve_time_s: '求解时间(s)', error: '错误信息', error_message: '错误信息',
    output_power_w: '输出功率(W)', input_power_w: '输入功率(W)',
    em_power_w: '电磁功率(W)', power_factor: '功率因数',
    back_emf_v: '反电动势(V)', back_emf_thd_pct: '反电动势谐波(%)',
    line_current_rms_a: '线电流(A)', phase_current_peak_a: '相电流峰值(A)',
    shaft_speed_rpm: '转速(rpm)', no_load_speed_rpm: '空载转速(rpm)',
    torque_constant: '转矩常数(Nm/A)', shaft_torque_nm: '轴转矩(Nm)',
    stall_torque_nm: '堵转转矩(Nm)', tmax_nm: '最大转矩(Nm)', tmin_nm: '最小转矩(Nm)',
    winding_temp_c: '绕组温度(°C)', winding_hotspot_temp_c: '绕组热点(°C)',
    magnet_temp_c: '磁钢温度(°C)', stator_temp_c: '定子温度(°C)',
    bearing_temp_c: '轴承温度(°C)', temp_rise_c: '温升(°C)',
    thermal_resistance_k_w: '热阻(K/W)',
    axial_force_n: '轴向力(N)', radial_force_n: '径向力(N)',
    max_stress_mpa: '最大应力(MPa)', deformation_mm: '最大变形(mm)',
  }
  return keys.map(k => ({
    key: k,
    label: labelMap[k] || k,
    width: k === 'error' ? 200 : 110,
  }))
})

// --- Result column settings (P2-4) ---
// Default: core metric columns + the plan's scan-variable input columns. All
// 35+ metrics remain available on demand via the column-settings dialog
// instead of being laid out unconditionally.
const showColumnSettings = ref(false)
const visibleColumnKeys = ref<string[] | null>(loadSavedColumns())  // null = use defaults

// Persist the column preference across sessions (localStorage). The choice is
// global (shared by all plans) since metric columns are plan-independent.
const COLUMN_STORAGE_KEY = 'afm:plan-result-columns'

function loadSavedColumns(): string[] | null {
  try {
    const raw = localStorage.getItem(COLUMN_STORAGE_KEY)
    if (!raw) return null
    const parsed = JSON.parse(raw)
    return Array.isArray(parsed) && parsed.length ? parsed : null
  } catch {
    return null
  }
}

function persistColumns() {
  try {
    if (visibleColumnKeys.value === null) {
      localStorage.removeItem(COLUMN_STORAGE_KEY)
    } else {
      localStorage.setItem(COLUMN_STORAGE_KEY, JSON.stringify(visibleColumnKeys.value))
    }
  } catch { /* storage may be unavailable; non-fatal */ }
}

const defaultColumnKeys = computed(() => {
  const keys = coreResultColumns.map(c => c.key)
  scanVariables.value.forEach((v: any) => {
    if (v?.name && !keys.includes(v.name)) keys.push(v.name)
  })
  // Thermal columns join the default view when the task ran with thermal
  // analysis (thermal_mode steady/coupled) and the results carry them.
  const thermalCore = [
    'winding_temp_c', 'winding_hotspot_temp_c', 'magnet_temp_c',
    'stator_temp_c', 'temp_rise_c',
  ]
  if (results.value.length) {
    const present = new Set(Object.keys(results.value[0]))
    thermalCore.forEach(k => {
      if (present.has(k) && !keys.includes(k)) keys.push(k)
    })
  }
  return keys
})

const displayColumns = computed(() => {
  const all = allResultColumns.value
  const sel = new Set(visibleColumnKeys.value ?? defaultColumnKeys.value)
  return all.filter(c => sel.has(c.key))
})

const openColumnSettings = () => {
  if (visibleColumnKeys.value === null) {
    visibleColumnKeys.value = [...defaultColumnKeys.value]
  }
  showColumnSettings.value = true
}

const confirmColumnSettings = () => {
  if (visibleColumnKeys.value !== null && !visibleColumnKeys.value.length) {
    ElMessage.warning('至少保留一列')
    return
  }
  persistColumns()
  showColumnSettings.value = false
}

const resetColumns = () => {
  visibleColumnKeys.value = null
  persistColumns()
  showColumnSettings.value = false
}

// Methods
const calcPoints = (v: any) => {
  // Prefer the explicit values list (plan_data.variables / AI-generated plans
  // store values arrays); fall back to a min/max/step range otherwise so both
  // legacy and current plan shapes compute a non-zero point count (P0-1).
  if (Array.isArray(v.values) && v.values.length) return v.values.length
  const step = v.step ?? v.step_size
  if (!step || step <= 0) return 0
  const min = v.min_value ?? v.start
  const max = v.max_value ?? v.stop
  if (min == null || max == null) return 0
  return Math.floor((max - min) / step + 1e-9) + 1
}

const getDefaultValue = (name: string) => {
  const def = ALL_FIXED_PARAM_TEMPLATE.find(t => t.name === name)
  return def ? def.value : '-'
}

const fixedParamsByCategory = (cat: string) => {
  return fixedParams.value.filter((p: any) => String(p.category_cn || p.category) === cat)
}

const toggleCategory = (cat: string) => {
  // Categories are expanded by default; the map only records explicit collapse.
  expandedCategories[cat] = expandedCategories[cat] === false
}

const isFailed = (row: any) => row.status === 'FAILED' || row.status === 'failed'

const formatCell = (val: any) => {
  if (val === null || val === undefined) return '-'
  if (typeof val === 'number') return Number.isInteger(val) ? val : val.toFixed(4)
  return String(val)
}

const goBack = () => router.back()

const onVarSelect = (name: string) => {
  const template = variableCatalog.value?.template || ALL_FIXED_PARAM_TEMPLATE
  const p = template.find((t: any) => t.name === name)
  if (p) {
    newVar.name_cn = p.name_cn || p.name
    newVar.unit = p.unit
    const baseVal = Number(p.value) || 1
    newVar.min_value = baseVal * 0.8
    newVar.max_value = baseVal * 1.2
    newVar.step = Math.abs(baseVal * 0.05) || 0.1
  }
}

// Ensure plan_data.variables exists and return it (scan variables are stored
// under plan_data.variables in the plan payload).
const ensureVariables = () => {
  if (!plan.plan_data) plan.plan_data = {}
  if (!Array.isArray(plan.plan_data.variables)) plan.plan_data.variables = []
  return plan.plan_data.variables
}

const confirmAddVar = () => {
  if (!newVar.name) { ElMessage.warning('请选择变量名'); return }
  ensureVariables().push({ ...newVar })
  scanVarsDirty.value = true
  showAddVar.value = false
  Object.assign(newVar, { name: '', name_cn: '', min_value: 0, max_value: 1, step: 0.1, unit: '' })
  ElMessage.success('变量已添加，点击"保存修改"后生效')
}

const removeVariable = (idx: number) => {
  ensureVariables().splice(idx, 1)
  scanVarsDirty.value = true
}

// Persist scan-variable edits to the backend (full plan_data replacement,
// validated server-side by the single-source plan schema).
const saveScanVariables = async () => {
  savingVars.value = true
  try {
    const planData = { ...(plan.plan_data || {}), variables: ensureVariables() }
    await api.put(`/plans/${planId.value}`, { plan_data: planData })
    scanVarsDirty.value = false
    ElMessage.success('方案参数已保存')
    await loadPlan()
  } catch (e: any) {
    ElMessage.error('保存失败: ' + (e.message || e))
  } finally {
    savingVars.value = false
  }
}

// Boundary-condition editing: inline edit mode on the plan's BC. Values the
// user changes are tagged "edited" in bc_meta so the source stays traceable.
const bcEditing = ref(false)
const bcSaving = ref(false)
const bcEditForm = reactive<Record<string, any>>({})

const startBcEdit = () => {
  const bc = plan.plan_data?.boundary_conditions || {}
  bcFieldCatalog.value.forEach(f => { bcEditForm[f.key] = bc[f.key] ?? null })
  bcEditing.value = true
}

const saveBcEdit = async () => {
  bcSaving.value = true
  try {
    const oldBc = plan.plan_data?.boundary_conditions || {}
    const oldMeta = { ...(plan.plan_data?.bc_meta || {}) }
    const newBc: Record<string, any> = {}
    for (const [k, v] of Object.entries(bcEditForm)) {
      if (v !== null && v !== undefined && v !== '') newBc[k] = v
    }
    for (const k of Object.keys(newBc)) {
      const changed = String(newBc[k]) !== String(oldBc[k] ?? '')
      if (changed || !oldMeta[k]) oldMeta[k] = { source: 'edited' }
    }
    const planData = {
      ...(plan.plan_data || {}),
      boundary_conditions: newBc,
      bc_meta: oldMeta,
    }
    await api.put(`/plans/${planId.value}`, { plan_data: planData })
    bcEditing.value = false
    ElMessage.success('边界条件已保存')
    await loadPlan()
  } catch (e: any) {
    ElMessage.error('保存失败: ' + (e.message || e))
  } finally {
    bcSaving.value = false
  }
}

// Fixed-parameter inline editing: enter edit mode -> all categories expand,
// value cells become inputs; save writes the full fixed_params list back via
// PUT plan_data (server-side schema validation applies).
const fixedEditing = ref(false)
const fixedSaving = ref(false)
const fixedEditValues = reactive<Record<string, any>>({})

const startFixedEdit = () => {
  ;(fixedParams.value as any[]).forEach((p: any) => { fixedEditValues[p.name] = p.value })
  fixedEditing.value = true
}

const isFixedModified = (row: any) => {
  const def = ALL_FIXED_PARAM_TEMPLATE.find(t => t.name === row.name)
  if (!def) return true
  return String(row.value) !== String(def.value)
}

const saveFixedParams = async () => {
  fixedSaving.value = true
  try {
    const newFixed = (fixedParams.value as any[]).map((p: any) => ({
      ...p,
      value: fixedEditValues[p.name] !== undefined ? fixedEditValues[p.name] : p.value,
    }))
    const planData = { ...(plan.plan_data || {}), fixed_params: newFixed }
    await api.put(`/plans/${planId.value}`, { plan_data: planData })
    fixedEditing.value = false
    ElMessage.success('固定参数已保存')
    await loadPlan()
  } catch (e: any) {
    ElMessage.error('保存失败: ' + (e.message || e))
  } finally {
    fixedSaving.value = false
  }
}

// Pre-flight checklist: key -> localized label (backend returns keys only).
const PREFLIGHT_LABELS: Record<string, string> = {
  model_path: '模型文件',
  unverified_vars: '变量名确认',
  scan_vars: '扫描变量',
  point_count: '数据点规模',
  executor: '本地执行器',
}

function preflightMessage(c: any): string {
  switch (c.key) {
    case 'model_path':
      return c.status === 'pass' ? `已就绪：${c.value}` : (c.value ? `模型文件不存在：${c.value}` : '未设置模型路径（model_path 为空）')
    case 'unverified_vars':
      return c.status === 'pass' ? '全部固定参数变量名已映射' : `${(c.items || []).length} 个变量名未经 Motor-CAD 验证：${(c.items || []).join('、')}`
    case 'scan_vars':
      return c.status === 'pass' ? `${c.count} 个扫描变量` : '未定义扫描变量（至少 1 个）'
    case 'point_count':
      return c.status === 'warn' ? `${c.count} 个数据点，规模较大，请确认` : `${c.count} 个数据点`
    case 'executor':
      return c.online > 0 ? `${c.online} 个执行器在线` : '未检测到在线执行器（任务将排队等待）'
    default:
      return ''
  }
}

// P6: task-level thermal mode (off/steady/coupled), chosen at one-click start.
const thermalMode = ref('steady')

const startSimulation = async () => {
  // Run the pre-flight checklist first; the start button in the dialog is
  // disabled while any check has status 'fail'.
  preflightLoading.value = true
  try {
    const res: any = await api.get(`/plans/${planId.value}/preflight`)
    preflight.value = res
    showPreflight.value = true
  } catch (e: any) {
    ElMessage.error('仿真前检查失败: ' + (e.message || e))
  } finally {
    preflightLoading.value = false
  }
}

const confirmStartSimulation = async () => {
  showPreflight.value = false
  starting.value = true
  try {
    await api.post(`/plans/${planId.value}/start-simulation`, {
      thermal_mode: thermalMode.value,
    })
    ElMessage.success('仿真已启动')
    isRunning.value = true
    activeTab.value = 'overview'
  } catch (e: any) {
    ElMessage.error('启动失败: ' + (e.message || e))
  } finally {
    starting.value = false
  }
}

// One-click repair for a failed model_path check: write the topology's
// default base model into the plan, then re-run preflight so the checklist
// flips green in place.
const applyDefaultModel = async (defaultModel: string) => {
  applyingDefaultModel.value = true
  try {
    const planData = { ...(plan.plan_data || {}), model_path: defaultModel }
    await api.put(`/plans/${planId.value}`, { plan_data: planData })
    ElMessage.success('已应用拓扑默认模型')
    await loadPlan()
    const res: any = await api.get(`/plans/${planId.value}/preflight`)
    preflight.value = res
  } catch (e: any) {
    ElMessage.error('应用默认模型失败: ' + (e.message || e))
  } finally {
    applyingDefaultModel.value = false
  }
}

const stopSimulation = async () => {
  try {
    await ElMessageBox.confirm(
      '确认停止当前仿真？已完成的点会保留，执行器将在当前点结束后中断。',
      '停止仿真',
      { type: 'warning' },
    )
  } catch {
    return // user dismissed the confirm dialog
  }
  stopping.value = true
  try {
    const tid = activeTask.value?.task_id
    if (tid) {
      await api.post(`/tasks/${tid}/cancel`)
    }
    // Refresh from backend: cancel flips the task status, so the progress
    // banner (driven by activeTask.total_points snapshot) disappears.
    await loadActiveTask()
    ElMessage.success('已发送停止指令，执行器将在当前点结束后中断')
  } catch (e: any) {
    ElMessage.error('停止失败: ' + (e.message || e))
  } finally {
    stopping.value = false
  }
}

const onFileChange = (file: any) => {
  uploadFile.value = file.raw
}

const confirmUpload = async () => {
  if (!uploadFile.value) return
  uploading.value = true
  try {
    const formData = new FormData()
    formData.append('file', uploadFile.value)
    await api.post(`/plans/${planId.value}/upload-results`, formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    })
    ElMessage.success('结果上传成功')
    showUpload.value = false
    loadResults()
  } catch (e: any) {
    ElMessage.error('上传失败: ' + (e.message || e))
  } finally {
    uploading.value = false
  }
}

// Download the result set as CSV (same columns as the Excel export).
// NOTE: /api/plans/{id}/download returns the *plan definition* JSON for the
// executor, not results — do not point this button at that endpoint.
const downloadResults = () => {
  window.open(`/api/plans/${planId.value}/export-csv`, '_blank')
}

// Export the full result set (scan info + EM + thermal columns, two-row
// header format matching the torqrippswap scan_results.xlsx convention).
const exportExcel = () => {
  window.open(`/api/plans/${planId.value}/export-xlsx`, '_blank')
}

const runAIAnalysis = async () => {
  analyzing.value = true
  try {
    const res: any = await api.post(`/plans/${planId.value}/ai-analyze`, {}, { timeout: 60000 })
    aiAnalysis.value = res
    aiStep.value = 1
    ElMessage.success('AI 分析完成')
  } catch (e: any) {
    ElMessage.error('分析失败: ' + (e.message || e))
  } finally {
    analyzing.value = false
  }
}

const generateIteration = async () => {
  generating.value = true
  try {
    const res: any = await api.post(`/plans/${planId.value}/generate-iteration`, {}, { timeout: 60000 })
    iterationPlan.value = res
    aiStep.value = 2
    ElMessage.success('迭代方案已生成')
  } catch (e: any) {
    ElMessage.error('生成失败: ' + (e.message || e))
  } finally {
    generating.value = false
  }
}

const viewIteration = () => {
  if (iterationPlan.value?.id) {
    router.push(`/plans/${iterationPlan.value.id}`)
  }
}

const extractExperience = async () => {
  extracting.value = true
  try {
    await api.post(`/experience/from-plan/${planId.value}/smart-extract`, {})
    experienceExtracted.value = true
    aiStep.value = 3
    ElMessage.success('经验已提取')
  } catch (e: any) {
    ElMessage.error('提取失败: ' + (e.message || e))
  } finally {
    extracting.value = false
  }
}

// Data loading
const loadPlan = async () => {
  try {
    const res: any = await api.get(`/plans/${planId.value}`)
    Object.assign(plan, res)
  } catch (e: any) {
    ElMessage.error('加载方案失败: ' + (e.message || e))
  }
}

const loadResults = async () => {
  try {
    const res: any = await api.get(`/plans/${planId.value}/results`)
    const raw = res.items || res.results || res.data || []
    // Flatten nested params/metrics into a flat object for table display.
    // Alias legacy metric names to the canonical afmcore keys (tavg_nm /
    // ripple_pct) so older results still populate the columns.
    results.value = raw.map((r: any) => {
      const flat = { ...r, ...(r.params || {}), ...(r.metrics || {}) }
      if (flat.tavg_nm == null && flat.average_torque_nm != null) flat.tavg_nm = flat.average_torque_nm
      if (flat.ripple_pct == null && flat.torque_ripple_pct != null) flat.ripple_pct = flat.torque_ripple_pct
      return flat
    })
  } catch {
    results.value = []
  }
}

const loadActiveTask = async () => {
  try {
    const res: any = await api.get(`/plans/${planId.value}/active-task`)
    activeTask.value = res.task || res
    isRunning.value = activeTask.value && ['running', 'dispatched'].includes(activeTask.value.status)
  } catch {
    activeTask.value = null
    isRunning.value = false
  }
}

const loadBcFieldCatalog = async () => {
  try {
    const res: any = await api.get('/bc-fields')
    bcFieldCatalog.value = res.fields || []
  } catch { /* optional: boundary display stays empty */ }
}

const loadSolveTimeStats = async () => {
  try {
    const res: any = await api.get('/analytics/solve-time-stats')
    solveTimeStats.value = res
  } catch { /* optional: fall back to heuristic estimate */ }
}

let pollTimer: any = null

onMounted(() => {
  loadPlan()
  loadResults()
  loadActiveTask()
  loadVariableCatalog()
  loadBcFieldCatalog()
  loadSolveTimeStats()
  pollTimer = setInterval(() => {
    if (isRunning.value) {
      loadActiveTask()
      loadResults()
    }
  }, 5000)
})

onUnmounted(() => {
  if (pollTimer) clearInterval(pollTimer)
})

watch(activeTab, (tab) => {
  if (tab === 'results') loadResults()
})
</script>

<style scoped>
.plan-detail {
  padding: var(--space-6);
  max-width: 1600px;
  margin: 0 auto;
}

/* Progress Banner */
.progress-banner {
  background: linear-gradient(135deg, var(--color-primary-bg), #e0e7ff);
  border: 1px solid var(--color-primary-light);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
}

.progress-info {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.progress-title {
  font-weight: 600;
  color: var(--color-primary-dark);
}

.progress-detail {
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
}

.progress-bar {
  margin: 0;
}

/* Tabs */
.plan-tabs {
  --el-tabs-header-height: 48px;
}

.plan-tabs :deep(.el-tabs__header) {
  margin-bottom: var(--space-5);
  border-bottom: 2px solid var(--color-border-light);
}

.plan-tabs :deep(.el-tabs__nav-wrap::after) {
  display: none;
}

.plan-tabs :deep(.el-tabs__item) {
  font-size: var(--font-size-base);
  font-weight: 500;
  height: 48px;
  line-height: 48px;
}

/* Stat row */
.stat-row {
  margin-bottom: var(--space-5);
}

/* AI reasoning */
.ai-reasoning {
  font-size: var(--font-size-sm);
  line-height: 1.8;
  color: var(--color-text-secondary);
  white-space: pre-wrap;
  background: var(--color-border-light);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border-left: 3px solid var(--color-primary);
}

/* Criteria */
.criteria-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.criteria-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
}

.criteria-icon {
  color: var(--color-success);
  flex-shrink: 0;
}

/* Quick actions */
.quick-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
}

.quick-action-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  cursor: pointer;
  transition: all var(--transition-fast);
  text-align: center;
}

.quick-action-card:hover {
  border-color: var(--color-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.qa-title {
  font-weight: 600;
  font-size: var(--font-size-base);
  color: var(--color-text-primary);
  margin-top: var(--space-2);
}

.qa-desc {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

/* Fixed params */
.fixed-summary {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-right: var(--space-3);
}

.fixed-modified {
  margin-bottom: var(--space-5);
}

.fixed-group-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-warning);
  margin-bottom: var(--space-3);
}

.modified-value {
  font-weight: 600;
  color: var(--color-primary);
}

.var-code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--color-border-light);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
}

.fixed-category {
  margin-bottom: var(--space-3);
}

.fixed-category-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-border-light);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-secondary);
  transition: background var(--transition-fast);
}

.fixed-category-title:hover {
  background: var(--color-primary-light);
}

.cat-arrow {
  font-size: 12px;
  transition: transform var(--transition-fast);
}

.cat-count {
  font-weight: 400;
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
}

/* AI steps */
.ai-steps {
  margin-bottom: var(--space-6);
  padding: var(--space-5);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}

.ai-analysis-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.ai-section {
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.ai-section-title {
  background: var(--color-border-light);
  padding: var(--space-2) var(--space-4);
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-secondary);
}

.ai-section-body {
  padding: var(--space-4);
  font-size: var(--font-size-sm);
  line-height: 1.7;
  color: var(--color-text-secondary);
  white-space: pre-wrap;
}

.iteration-content,
.experience-content {
  padding: var(--space-2) 0;
}

/* Upload */
:deep(.el-upload-dragger) {
  border-radius: var(--radius-lg);
}

/* Pre-flight checklist */
.preflight-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.thermal-mode-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.thermal-mode-label {
  font-size: 13px;
  color: var(--color-text-secondary);
  font-weight: 500;
}
.preflight-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.preflight-icon {
  font-size: 20px;
  margin-top: 2px;
}
.preflight-item.is-pass .preflight-icon { color: var(--color-success); }
.preflight-item.is-warn .preflight-icon { color: var(--color-warning); }
.preflight-item.is-fail .preflight-icon { color: var(--color-danger); }
.preflight-item.is-fail { border-color: var(--color-danger); background: #fef2f2; }
.preflight-label {
  font-weight: 600;
  font-size: var(--font-size-sm);
  color: var(--color-text-primary);
}
.preflight-message {
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
  margin-top: 2px;
  word-break: break-all;
}

.preflight-fix {
  margin-top: 6px;
}

.est-basis {
  margin-top: 4px;
  font-size: 11px;
  color: var(--color-text-muted);
  text-align: center;
}

/* Result column settings */
.column-settings-tip {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}
.column-settings-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 16px;
  max-height: 400px;
  overflow-y: auto;
}
.column-settings-item {
  margin-right: 0;
}

/* Failed-status cell with error tooltip */
.status-failed-cell {
  cursor: help;
  border-bottom: 1px dashed var(--color-danger);
}

.bc-source-tag {
  margin-left: 6px;
  font-size: 10px;
  height: 18px;
  line-height: 16px;
  padding: 0 4px;
}

.criteria-text {
  flex: 1;
}

.criteria-origin {
  margin-left: 8px;
  flex-shrink: 0;
}

.var-values {
  font-family: var(--font-mono, monospace);
  font-size: 12px;
}
</style>
