<template>
  <div class="price-page student-training-shell">
    <main class="training-core">
      <section class="task-header">
        <p class="task-kicker">PRODUCT PRICING</p>
        <h1>{{ taskTitle }}</h1>
      </section>

      <TrainingTaskBrief
        :background="taskBackground"
        :goals="taskGoals"
        :requirements="taskRequirement"
        :show-material="false"
      />

      <section class="price-workbench">
        <nav class="step-indicator" aria-label="新产品定价步骤">
          <template v-for="step in pricingSteps" :key="step.no">
            <button
              type="button"
              class="step-tab"
              :class="{ active: currentStep === step.no, complete: currentStep > step.no }"
              :aria-current="currentStep === step.no ? 'step' : undefined"
              @click="switchStep(step.no)"
            >
              <span class="step-marker">{{ String(step.no).padStart(2, "0") }}</span>
              <span class="step-copy">
                <strong>{{ step.title }}</strong>
                <span>{{ currentStep === step.no ? "当前步骤" : "点击切换" }}</span>
              </span>
            </button>
            <span
              v-if="step.no < pricingSteps.length"
              class="step-connector"
              :class="{ complete: currentStep > step.no }"
              aria-hidden="true"
            />
          </template>
        </nav>

        <header class="workbench-head">
          <div>
            <p class="section-eyebrow">STEP {{ String(currentStep).padStart(2, "0") }}</p>
        <h2>{{ currentStepTitle }}</h2>
          </div>
          <TrainingMaterialButton
            :material-name="taskConfig?.materialName"
            :material-url="taskConfig?.materialUrl"
          />
        </header>

        <template v-if="currentStep === 1">
          <p class="intro-copy">{{ labels.formulaTip }}</p>

          <section class="unit-cost-panel" :aria-label="labels.unitCost">
            <div class="cost-panel-copy">
              <span class="cost-dot"></span>
              <div>
                <p>{{ labels.unitCost }}</p>
                <small>{{ labels.unitCostHelp }}</small>
              </div>
            </div>
            <el-input
              v-model="unitCost"
              class="unit-cost-input"
              inputmode="decimal"
              :placeholder="labels.unitCostPlaceholder"
              @input="clearValidation"
            >
              <template #append>{{ labels.yuan }}</template>
            </el-input>
          </section>

          <section class="pricing-table-wrap">
            <table class="pricing-table">
              <thead>
                <tr>
                  <th>{{ labels.serial }}</th>
                  <th>{{ labels.channel }}</th>
                  <th>{{ labels.markupRate }}</th>
                  <th>{{ labels.salePrice }}</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="(row, index) in pricingRows" :key="row.id">
                  <td class="serial-cell">{{ String(index + 1).padStart(2, "0") }}</td>
                  <td class="channel-cell">{{ row.channel }}</td>
                  <td>
                    <el-input
                      v-model="row.markupRate"
                      class="markup-input"
                      inputmode="decimal"
                      :placeholder="ratePlaceholder(row)"
                      @input="clearValidation"
                    >
                      <template #append>%</template>
                    </el-input>
                    <small class="range-hint">{{ rateHint(row) }}</small>
                  </td>
                  <td class="sale-price-cell">
                    <span v-if="salePrice(row) !== null">{{ labels.yuanSign }} {{ formatMoney(salePrice(row)) }}</span>
                    <span v-else class="empty-price">{{ labels.waitForInput }}</span>
                  </td>
                </tr>
              </tbody>
            </table>
          </section>

          <p v-if="validationMessage" class="validation-message">{{ validationMessage }}</p>

          <footer class="workbench-footer">
            <p>{{ labels.footerTip }}</p>
            <div class="footer-actions">
              <el-button plain :disabled="saving" @click="resetStepOne">{{ labels.clear }}</el-button>
              <el-button type="primary" :loading="saving" @click="saveStepOne">{{ labels.saveStepOne }}</el-button>
            </div>
          </footer>
        </template>

        <template v-else-if="currentStep === 3">
          <section class="value-pricing-section">
            <div class="value-pricing-toolbar">
              <p>{{ labels.valueBasedPricingIntro }}</p>
              <div class="toolbar-actions">
                <button type="button" class="primary-button" @click="addPerceivedValueItem">{{ labels.valueBasedAddRow }}</button>
                <button type="button" class="secondary-button" :disabled="!canRemovePerceivedValueItem" @click="removePerceivedValueItem">
                  {{ labels.valueBasedRemoveRow }}
                </button>
              </div>
            </div>

            <div class="pricing-table-wrap">
              <table class="pricing-table perceived-value-table">
                <thead>
                  <tr>
                    <th>{{ labels.perceivedValueItem }}</th>
                    <th>{{ labels.perceivedValueAmount }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="row in perceivedValueItems" :key="row.id">
                    <td><el-input v-model="row.name" :placeholder="labels.perceivedValueItemPlaceholder" @input="clearValidation" /></td>
                    <td>
                      <el-input v-model="row.perceivedValue" inputmode="decimal" :placeholder="labels.perceivedValueAmountPlaceholder" @input="clearValidation">
                        <template #append>{{ labels.yuan }}</template>
                      </el-input>
                    </td>
                  </tr>
                  <tr class="total-row">
                    <td>{{ labels.perceivedValueTotal }}</td>
                    <td class="sale-price-cell">
                      <span v-if="perceivedValueTotal !== null">{{ labels.yuanSign }} {{ formatMoney(perceivedValueTotal) }}</span>
                      <span v-else class="empty-price">{{ labels.waitForInput }}</span>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>

            <section class="value-pricing-result">
              <label>
                <span>{{ labels.profitDiscountRate }}</span>
                <el-input v-model="perceivedValueDiscountRate" inputmode="decimal" :placeholder="labels.discountRatePlaceholder" @input="clearValidation">
                  <template #append>%</template>
                </el-input>
              </label>
              <div class="value-price-output">
                <span>{{ labels.valueBasedPrice }}</span>
                <strong v-if="valueBasedPrice !== null">{{ labels.yuanSign }} {{ formatMoney(valueBasedPrice) }}</strong>
                <em v-else>{{ labels.waitForInput }}</em>
              </div>
              <p>{{ labels.valueBasedPriceFormula }}</p>
            </section>

            <p v-if="validationMessage" class="validation-message">{{ validationMessage }}</p>

            <footer class="workbench-footer">
              <p>{{ labels.valueBasedFooterTip }}</p>
              <div class="footer-actions">
                <el-button plain :disabled="saving" @click="resetStepThree">{{ labels.clearStepThree }}</el-button>
                <el-button type="primary" :loading="saving" @click="saveStepThree">{{ labels.saveStepThree }}</el-button>
              </div>
            </footer>
          </section>
        </template>

        <template v-else-if="currentStep === 4">
          <section class="psm-section">
            <div class="psm-toolbar">
              <div>
                <p>{{ labels.psmIntro }}</p>
                <p class="psm-subtitle">{{ labels.psmDataTitle }}</p>
              </div>
              <div class="toolbar-actions">
                <button type="button" class="primary-button" @click="addPsmRow">{{ labels.psmAddRow }}</button>
                <button type="button" class="secondary-button" :disabled="!canRemovePsmRow" @click="removePsmRow">
                  {{ labels.psmRemoveRow }}
                </button>
                <button type="button" class="secondary-button" @click="exportPsmTable">{{ labels.psmExport }}</button>
              </div>
            </div>

            <div class="table-scroll">
              <table class="price-table psm-table">
                <thead>
                  <tr>
                    <th>{{ labels.psmPriceRange }}</th>
                    <th>{{ labels.psmTooExpensive }}</th>
                    <th>{{ labels.psmTooCheap }}</th>
                    <th>{{ labels.psmExpensiveAcceptable }}</th>
                    <th>{{ labels.psmCheapValue }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="row in psmRows" :key="row.id">
                    <td>
                      <span v-if="row.isDefault">{{ row.priceRange }}</span>
                      <input v-else v-model="row.priceRange" type="text" :placeholder="labels.psmCustomRangePlaceholder" />
                    </td>
                    <td><input v-model="row.tooExpensive" type="number" min="0" max="100" step="0.01" :placeholder="labels.psmPercentagePlaceholder" @input="clearValidation" /></td>
                    <td><input v-model="row.tooCheap" type="number" min="0" max="100" step="0.01" :placeholder="labels.psmPercentagePlaceholder" @input="clearValidation" /></td>
                    <td><input v-model="row.expensiveAcceptable" type="number" min="0" max="100" step="0.01" :placeholder="labels.psmPercentagePlaceholder" @input="clearValidation" /></td>
                    <td><input v-model="row.cheapValue" type="number" min="0" max="100" step="0.01" :placeholder="labels.psmPercentagePlaceholder" @input="clearValidation" /></td>
                  </tr>
                </tbody>
              </table>
            </div>

            <section class="psm-point-section">
              <h3>{{ labels.psmPricePointTitle }}</h3>
              <div class="psm-point-grid">
                <label>
                  <span>{{ labels.psmOptimalPrice }}</span>
                  <input v-model="psmPricePoints.optimalPrice" type="number" min="0" step="0.01" :placeholder="labels.psmPricePointPlaceholder" @input="clearValidation" />
                </label>
                <label>
                  <span>{{ labels.psmAcceptablePrice }}</span>
                  <input v-model="psmPricePoints.acceptablePrice" type="number" min="0" step="0.01" :placeholder="labels.psmPricePointPlaceholder" @input="clearValidation" />
                </label>
                <label>
                  <span>{{ labels.psmAcceptableRange }}</span>
                  <input v-model="psmPricePoints.acceptableRange" type="text" :placeholder="labels.psmPricePointPlaceholder" @input="clearValidation" />
                </label>
              </div>
            </section>

            <section class="psm-chart-section">
              <div class="psm-chart-head">
                <div>
                  <h3>{{ labels.psmChartTitle }}</h3>
                  <p>{{ labels.psmChartHint }}</p>
                </div>
                <button type="button" class="primary-button" :disabled="uploadingPsmChart" @click="triggerPsmChartUpload">
                  {{ uploadingPsmChart ? labels.uploading : labels.psmUploadChart }}
                </button>
              </div>
              <input ref="psmChartInput" class="file-picker" type="file" accept="image/*" @change="handlePsmChartUpload" />
              <div v-if="psmChart.url" class="psm-chart-preview">
                <el-image
                  :src="psmChart.url"
                  :preview-src-list="psmChartPreviewList"
                  fit="cover"
                  class="psm-chart-image"
                  preview-teleported
                />
                <div>
                  <p>{{ psmChart.name }}</p>
                  <button type="button" class="text-button" @click="removePsmChart">{{ labels.psmChartRemove }}</button>
                </div>
              </div>
              <div class="psm-guide-row">
                <p>{{ labels.psmGuideTip }}</p>
                <button type="button" class="secondary-button" @click="downloadPsmGuide">{{ labels.psmDownloadGuide }}</button>
              </div>
            </section>

            <p v-if="validationMessage" class="validation-message">{{ validationMessage }}</p>

            <div class="step-footer">
              <button type="button" class="primary-button" :disabled="saving" @click="saveStepFour">
                {{ saving ? labels.psmSaving : labels.psmSave }}
              </button>
            </div>
          </section>
        </template>

        <template v-else-if="currentStep === 5">
          <section class="final-pricing-section">
            <p class="intro-copy final-pricing-intro">{{ labels.finalPricingIntro }}</p>

            <div class="final-pricing-channel">
              <div>
                <h3>{{ labels.costMarkupChannel }}</h3>
                <p>{{ labels.costMarkupChannelHint }}</p>
              </div>
              <el-select v-model="selectedCostMarkupChannel" :placeholder="labels.costMarkupChannelPlaceholder" @change="clearValidation">
                <el-option
                  v-for="option in costMarkupChannelOptions"
                  :key="option.value"
                  :label="option.label"
                  :value="option.value"
                  :disabled="option.disabled"
                />
              </el-select>
            </div>

            <div class="pricing-table-wrap">
              <table class="pricing-table final-pricing-table">
                <thead>
                  <tr>
                    <th>{{ labels.pricingMethod }}</th>
                    <th>{{ labels.measuredResult }}</th>
                    <th>{{ labels.grossMarginRate }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="result in stepFiveMethodResults" :key="result.key">
                    <td>{{ result.methodName }}</td>
                    <td>{{ result.measuredPrice === null ? labels.waitForInput : `${labels.yuanSign} ${formatMoney(result.measuredPrice)}` }}</td>
                    <td>{{ result.grossMarginRate === null ? labels.waitForInput : `${formatMoney(result.grossMarginRate)}%` }}</td>
                  </tr>
                </tbody>
              </table>
            </div>

            <section class="final-analysis-section">
              <h3>{{ labels.finalAnalysisTitle }}</h3>
              <el-input
                v-model="finalPricingAnalysis"
                type="textarea"
                :autosize="{ minRows: 7, maxRows: 12 }"
                :placeholder="labels.finalAnalysisPlaceholder"
                @input="clearValidation"
              />
            </section>

            <p v-if="validationMessage" class="validation-message">{{ validationMessage }}</p>
            <div class="step-footer">
              <button type="button" class="primary-button" :disabled="saving" @click="saveStepFive">
                {{ saving ? labels.finalPricingSaving : labels.finalPricingSave }}
              </button>
            </div>
          </section>
        </template>

        <template v-else-if="currentStep === 2">
          <p class="intro-copy">{{ labels.competitorIntro }}</p>

          <section class="competitor-section">
            <div class="subsection-title">
              <span>01</span>
              <h3>{{ labels.competitorAnalysis }}</h3>
            </div>
            <div class="pricing-table-wrap">
              <table class="pricing-table competitor-table">
                <thead>
                  <tr>
                    <th>{{ labels.competitorProductType }}</th>
                    <th>{{ labels.competitorBrand }}</th>
                    <th>{{ labels.competitorEcommercePrice }}</th>
                    <th>{{ labels.competitorDifferentiator }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="row in competitorRows" :key="row.id">
                    <td>
                      <el-input v-model="row.productType" :placeholder="row.label" @input="clearValidation" />
                    </td>
                    <td>
                      <el-input v-model="row.brand" :placeholder="labels.brandPlaceholder" @input="clearValidation" />
                    </td>
                    <td>
                      <el-input v-model="row.ecommercePrice" inputmode="decimal" :placeholder="labels.pricePlaceholder" @input="clearValidation">
                        <template #append>{{ labels.yuan }}</template>
                      </el-input>
                    </td>
                    <td>
                      <el-input v-model="row.differentiator" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" :placeholder="labels.differentiatorPlaceholder" @input="clearValidation" />
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
          </section>

          <section class="competitor-section">
            <div class="subsection-title">
              <span>02</span>
              <h3>{{ labels.decisionTitle }}</h3>
            </div>
            <div class="pricing-table-wrap">
              <table class="pricing-table decision-table">
                <thead>
                  <tr>
                    <th>{{ labels.benchmarkCompetitor }}</th>
                    <th>{{ labels.targetPrice }}</th>
                    <th>{{ labels.grossMarginRate }}</th>
                    <th>{{ labels.competitorDifferentiator }}</th>
                    <th>{{ labels.pricingReason }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <td>
                      <el-select v-model="pricingDecision.benchmarkCompetitor" :placeholder="labels.benchmarkPlaceholder" @change="clearValidation">
                        <el-option
                          v-for="(row, index) in competitorRows"
                          :key="row.id"
                          :label="competitorOptionLabel(row, index)"
                          :value="row.productType"
                          :disabled="!String(row.productType || '').trim()"
                        />
                      </el-select>
                    </td>
                    <td>
                      <el-input v-model="pricingDecision.targetPrice" inputmode="decimal" :placeholder="labels.pricePlaceholder" @input="clearValidation">
                        <template #append>{{ labels.yuan }}</template>
                      </el-input>
                    </td>
                    <td class="sale-price-cell">
                      <span v-if="grossMarginRate !== null">{{ formatMoney(grossMarginRate) }}%</span>
                      <span v-else class="empty-price">{{ labels.waitForInput }}</span>
                    </td>
                    <td><el-input v-model="pricingDecision.differentiator" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" @input="clearValidation" /></td>
                    <td><el-input v-model="pricingDecision.pricingReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" @input="clearValidation" /></td>
                  </tr>
                </tbody>
              </table>
            </div>
          </section>

          <p v-if="validationMessage" class="validation-message">{{ validationMessage }}</p>

          <footer class="workbench-footer">
            <p>{{ labels.competitorFooterTip }}</p>
            <div class="footer-actions">
              <el-button plain :disabled="saving" @click="resetStepTwo">{{ labels.clearStepTwo }}</el-button>
              <el-button type="primary" :loading="saving" @click="saveStepTwo">{{ labels.saveStepTwo }}</el-button>
            </div>
          </footer>
        </template>
      </section>
    </main>

    <TrainingAiSidebar
      :task-key="TASK_KEY"
      :has-saved-answer="hasSavedAnswer"
      :progress-items="progressItems"
      :study-tip="labels.studyTip"
      :reference-title="labels.referenceTitle"
      :reference-text="labels.referenceText"
    />
  </div>
</template>

<script setup>
import { computed, getCurrentInstance, onMounted, reactive, ref } from "vue";
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
import {
  getStudentTrainingAnswer,
  saveStudentTrainingAnswer,
  uploadStudentTrainingFile,
  validateProductPricingStepOne,
  validateProductPricingStepTwo,
  validateProductPricingStepThree,
  validateProductPricingStepFour,
  validateProductPricingStepFive,
} from "@/api/studentTrainingAnswer";
import { getTrainingTaskByKey } from "@/api/trainingTask";
import { parseTrainingArray } from "@/views/training/taskKeyMap";
import { isStudentDemo } from "@/utils/studentDemo";

const TASK_KEY = "product-pricing";
const { proxy } = getCurrentInstance();

const labels = {
  psmTitle: "PSM价格敏感度测试",
  psmIntro: "PSM（价格敏感度测试）通过“太贵不买、太便宜不买、偏高可接受、便宜划算”四类问题衡量用户价格敏感度。请根据案例资料提供的原始数据计算各价格区间累计占比。",
  psmDataTitle: "（一）根据案例资料填写 PSM 累计占比表",
  psmAddRow: "增加一行",
  psmRemoveRow: "删除一行",
  psmExport: "导出",
  psmPriceRange: "价格（元）",
  psmTooExpensive: "太贵不买累计占比（%）",
  psmTooCheap: "太便宜不买累计占比（%）",
  psmExpensiveAcceptable: "偏高可接受累计占比（%）",
  psmCheapValue: "便宜划算累计占比（%）",
  psmCustomRangePlaceholder: "填写价格区间",
  psmPercentagePlaceholder: "0-100",
  psmPricePointTitle: "确定价格点",
  psmOptimalPrice: "最优价格点",
  psmAcceptablePrice: "可接受价格点",
  psmAcceptableRange: "可接受价格范围",
  psmPricePointPlaceholder: "学生手动填写",
  psmChartTitle: "PSM价格数据可视化",
  psmChartHint: "上传绘制完成的 PSM 价格数据折线图，支持点击缩略图预览。",
  psmUploadChart: "上传PSM价格数据折线图",
  psmChartUploadSuccess: "图表上传成功",
  psmChartUploadError: "图表上传失败，请重试",
  psmChartRemove: "移除图表",
  psmGuideTip: "下载案例数据表，使用 Excel 计算累计占比并绘制 PSM 曲线；也可导出本页数据后结合 AI 工具分析。",
  psmDownloadGuide: "下载实训操作说明",
  psmSave: "保存PSM价格敏感度测试",
  psmSaving: "保存中...",
  psmSaveSuccess: "PSM价格敏感度测试已保存",
  psmRowRequired: "请完整填写第 {index} 行 PSM 数据。",
  psmPercentageRange: "第 {index} 行“{label}”累计占比应在 0%–100% 之间。",
  psmPricePointsRequired: "请完整填写最优价格点、可接受价格点和可接受价格范围。",
  finalPricingTitle: "最终量产定价",
  finalPricingIntro: "汇总四种定价方法的测算结果，综合分析确定最终量产定价，并说明定价依据与利润空间。",
  costMarkupChannel: "成本加成定价渠道",
  costMarkupChannelHint: "从第 1 步已计算的渠道加成售价中选择一个作为成本加成定价结果。",
  costMarkupChannelPlaceholder: "请选择第 1 步渠道售价",
  pricingMethod: "定价方法",
  measuredResult: "测算结果",
  finalAnalysisTitle: "最终量产定价分析",
  finalAnalysisPlaceholder: "请综合成本、竞品、用户感知价值和 PSM 价格敏感度，说明最终量产定价、定价依据与利润空间。",
  finalPricingSave: "保存最终量产定价",
  finalPricingSaving: "保存中...",
  finalPricingSaveSuccess: "最终量产定价已保存",
  finalPricingChannelRequired: "请选择用于汇总的成本加成定价渠道。",
  finalPricingPrerequisiteRequired: "请先完成前四步定价测算，确保四种定价方法均有测算结果和毛利润率。",
  finalPricingAnalysisRequired: "请填写最终量产定价分析。",
  uploading: "上传中...",
  pricingTitle: "\u6e20\u9053\u52a0\u6210\u5b9a\u4ef7",
  formulaTip: "\u6838\u5fc3\u903b\u8f91\uff1a\u603b\u6210\u672c \u00d7\uff081 + \u52a0\u6210\u7387\uff09= \u6700\u7ec8\u552e\u4ef7\u3002\u4f01\u4e1a\u6839\u636e\u6e20\u9053\u7c7b\u578b\u548c\u54c1\u724c\u5b9a\u4f4d\u786e\u5b9a\u52a0\u6210\u7387\uff0c\u5728\u6210\u672c\u57fa\u7840\u4e0a\u52a0\u6210\u5b9a\u4ef7\u3002\u8bf7\u6839\u636e\u6848\u4f8b\u8d44\u6599\u63d0\u4f9b\u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\uff0c\u8ba1\u7b97\u5404\u6e20\u9053\u52a0\u6210\u552e\u4ef7\u3002",
  unitCost: "\u5355\u53f0\u7efc\u5408\u6210\u672c\uff08\u5143\uff09",
  unitCostHelp: "\u7531\u5b66\u751f\u4f9d\u636e\u6848\u4f8b\u8d44\u6599\u5728\u672c\u9875\u586b\u5199\uff0c\u4ec5\u7528\u4e8e\u672c\u9875\u81ea\u52a8\u8ba1\u7b97\u3002",
  unitCostPlaceholder: "\u8bf7\u8f93\u5165\u5355\u53f0\u7efc\u5408\u6210\u672c",
  yuan: "\u5143",
  yuanSign: "\u00a5",
  serial: "\u5e8f\u53f7",
  channel: "\u6e20\u9053",
  markupRate: "\u52a0\u6210\u7387\uff08%\uff09",
  salePrice: "\u52a0\u6210\u552e\u4ef7\uff08\u5143\uff09",
  rangePrefix: "\u793a\u4f8b\u586b\u5199\u8303\u56f4\uff1a",
  waitForInput: "\u7b49\u5f85\u586b\u5199",
  footerTip: "\u586b\u5199\u5355\u53f0\u7efc\u5408\u6210\u672c\u548c\u4e09\u4e2a\u6e20\u9053\u7684\u52a0\u6210\u7387\u540e\uff0c\u7cfb\u7edf\u5c06\u81ea\u52a8\u8ba1\u7b97\u52a0\u6210\u552e\u4ef7\u3002",
  clear: "\u6e05\u7a7a\u586b\u5199",
  save: "\u4fdd\u5b58\u672c\u6b65\u9aa4",
  studyTip: "\u5148\u6838\u5bf9\u6848\u4f8b\u4e2d\u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\uff0c\u518d\u7ed3\u5408\u5404\u6e20\u9053\u5b9a\u4f4d\u586b\u5199\u52a0\u6210\u7387\uff1b\u552e\u4ef7\u4f1a\u6309\u516c\u5f0f\u81ea\u52a8\u8ba1\u7b97\u3002",
  referenceTitle: "\u5b9a\u4ef7\u6838\u5bf9\u63d0\u793a",
  referenceText: "\u8bf7\u68c0\u67e5\u5355\u53f0\u7efc\u5408\u6210\u672c\u662f\u5426\u6765\u81ea\u6848\u4f8b\u8d44\u6599\uff0c\u5e76\u786e\u8ba4\u4e09\u4e2a\u6e20\u9053\u7684\u52a0\u6210\u7387\u5747\u5728\u9875\u9762\u7ed9\u51fa\u7684\u5efa\u8bae\u8303\u56f4\u5185\u3002",
  saveSuccess: "\u672c\u6b65\u9aa4\u5df2\u4fdd\u5b58",
  resetSuccess: "\u5df2\u6e05\u7a7a\u672c\u9875\u586b\u5199\u5185\u5bb9",
  costRequired: "\u8bf7\u586b\u5199\u5927\u4e8e 0 \u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\u3002",
  rateRequired: "\u8bf7\u586b\u5199\u201c{channel}\u201d\u7684\u52a0\u6210\u7387\u3002",
  rateRange: "\u201c{channel}\u201d\u7684\u52a0\u6210\u7387\u5e94\u5728 {min}%\u2013{max}% \u4e4b\u95f4\u3002",
  competitorPricingTitle: "\u7ade\u54c1\u4ef7\u683c\u5e26\u4e0e\u5b9a\u4ef7",
  competitorIntro: "\u8bf7\u57fa\u4e8e\u6848\u4f8b\u8d44\u6599\u5b8c\u6210 4 \u4e2a\u7ade\u54c1\u7684\u4ef7\u683c\u53ca\u5dee\u5f02\u5316\u5206\u6790\uff0c\u5e76\u9009\u62e9\u5bf9\u6807\u7ade\u54c1\u5236\u5b9a\u672c\u4ea7\u54c1\u5b9a\u4ef7\u3002",
  competitorAnalysis: "\u7ade\u54c1\u5dee\u5f02\u5316\u4f18\u52bf\u53ca\u552e\u4ef7\u5206\u6790",
  competitorProductType: "\u7ade\u54c1\u4ea7\u54c1\u7c7b\u578b",
  competitorBrand: "\u54c1\u724c",
  competitorEcommercePrice: "\u7535\u5546\u552e\u4ef7\uff08\u5143\uff09",
  competitorDifferentiator: "\u5dee\u5f02\u5316\u4f18\u52bf",
  brandPlaceholder: "\u8bf7\u586b\u5199\u54c1\u724c",
  pricePlaceholder: "\u8bf7\u586b\u5199\u91d1\u989d",
  differentiatorPlaceholder: "\u8bf7\u586b\u5199\u5dee\u5f02\u5316\u4f18\u52bf",
  decisionTitle: "\u786e\u5b9a\u5bf9\u6807\u7ade\u54c1\u53ca\u5b9a\u4ef7",
  benchmarkCompetitor: "\u5bf9\u6807\u7ade\u54c1",
  benchmarkPlaceholder: "\u8bf7\u5148\u586b\u5199\u5e76\u9009\u62e9\u5bf9\u6807\u7ade\u54c1",
  targetPrice: "\u5b9a\u4ef7\uff08\u5143\uff09",
  grossMarginRate: "\u6bdb\u5229\u6da6\u7387",
  pricingReason: "\u5b9a\u4ef7\u539f\u56e0\u5206\u6790",
  competitorFooterTip: "\u7ade\u54c1\u4fe1\u606f\u548c\u5b9a\u4ef7\u539f\u56e0\u5747\u9700\u5b8c\u6574\u586b\u5199\uff1b\u6bdb\u5229\u6da6\u7387\u4f1a\u6839\u636e\u7b2c 1 \u6b65\u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\u81ea\u52a8\u8ba1\u7b97\u3002",
  saveStepOne: "\u4fdd\u5b58\u5e76\u8fdb\u5165\u7b2c 2 \u6b65",
  clearStepTwo: "\u6e05\u7a7a\u7b2c 2 \u6b65",
  saveStepTwo: "\u4fdd\u5b58\u7b2c 2 \u6b65",
  stepOneSaveSuccess: "\u7b2c 1 \u6b65\u5df2\u4fdd\u5b58\uff0c\u5df2\u8fdb\u5165\u7b2c 2 \u6b65",
  stepTwoSaveSuccess: "\u7b2c 2 \u6b65\u5df2\u4fdd\u5b58",
  stepTwoResetSuccess: "\u5df2\u6e05\u7a7a\u7b2c 2 \u6b65\u586b\u5199\u5185\u5bb9",
  valueBasedPricingTitle: "用户感知价值定价",
  valueBasedPricingIntro: "基于用户感知价值定价。将产品可替代的功能或产品逐一折算价值并加总，再按一定折扣让渡利润给用户，确定最终售价。",
  valueBasedAddRow: "增加一行",
  valueBasedRemoveRow: "删除一行",
  perceivedValueItem: "可替代产品/功能/额外价值感知",
  perceivedValueAmount: "价值感知（元）",
  perceivedValueItemPlaceholder: "示例：闹钟",
  perceivedValueAmountPlaceholder: "示例：20",
  perceivedValueTotal: "感知价值合计",
  profitDiscountRate: "利润让渡折扣率",
  discountRatePlaceholder: "1-100",
  valueBasedPrice: "价值定价法价格",
  valueBasedPriceFormula: "价值定价法价格 = 感知价值合计 × 折扣率",
  valueBasedFooterTip: "首行示例可修改但不可删除；可增加或删除中间行，合计与价值定价法价格将自动计算。",
  clearStepThree: "清空第 3 步",
  saveStepThree: "保存第 3 步",
  stepThreeSaveSuccess: "第 3 步已保存",
  stepThreeResetSuccess: "已清空第 3 步填写内容",
  perceivedValueItemRequired: "请完整填写第 {index} 项价值感知。",
  perceivedValueAmountRequired: "第 {index} 项价值感知金额必须大于 0。",
  discountRateRequired: "利润让渡折扣率应大于 0 且不超过 100%。",
  costRequiredForStepTwo: "\u8bf7\u5148\u5728\u7b2c 1 \u6b65\u586b\u5199\u5927\u4e8e 0 \u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\u3002",
  competitorRequired: "\u8bf7\u5b8c\u6574\u586b\u5199\u7ade\u54c1 {index} \u7684\u4fe1\u606f\u3002",
  competitorPriceRequired: "\u8bf7\u586b\u5199\u7ade\u54c1 {index} \u5927\u4e8e 0 \u7684\u7535\u5546\u552e\u4ef7\u3002",
  benchmarkRequired: "\u8bf7\u586b\u5199\u5bf9\u6807\u7ade\u54c1\u3002",
  targetPriceRequired: "\u8bf7\u586b\u5199\u5927\u4e8e 0 \u7684\u5b9a\u4ef7\u3002",
  differentiatorRequired: "\u8bf7\u586b\u5199\u672c\u4ea7\u54c1\u7684\u5dee\u5f02\u5316\u4f18\u52bf\u3002",
  pricingReasonRequired: "\u8bf7\u586b\u5199\u5b9a\u4ef7\u539f\u56e0\u5206\u6790\u3002",
};

const DEFAULT_ROWS = [
  { id: "dealer", channel: "\u5382\u5bb6\u7ed9\u7ecf\u9500\u5546", minRate: 15, maxRate: 25 },
  { id: "direct-retail", channel: "\u5382\u5bb6\u76f4\u63a5\u7535\u5546\u96f6\u552e", minRate: 40, maxRate: 60 },
  { id: "premium-brand", channel: "\u9ad8\u7aef\u5b9a\u4f4d\u54c1\u724c", minRate: 70, maxRate: 80 },
];

const taskConfig = ref(null);
const saving = ref(false);
const hasSavedAnswer = ref(false);
const unitCost = ref("");
const pricingRows = ref(createPricingRows());
const validationMessage = ref("");
const currentStep = ref(1);
const competitorRows = ref(createCompetitorRows());
const pricingDecision = ref(createPricingDecision());
const DEFAULT_PERCEIVED_VALUE_ITEM = { id: "perceived-value-default", name: "闹钟", perceivedValue: "20" };
let perceivedValueItemSeed = 0;
const perceivedValueItems = ref(createPerceivedValueItems());
const perceivedValueDiscountRate = ref("");

const DEFAULT_PSM_PRICE_RANGES = [
  { id: "psm-under-100", priceRange: "≤100元" },
  { id: "psm-100-150", priceRange: "100~150元" },
  { id: "psm-150-200", priceRange: "150~200元" },
  { id: "psm-200-250", priceRange: "200~250元" },
  { id: "psm-250-300", priceRange: "250~300元" },
  { id: "psm-over-300", priceRange: "≥300元" },
];
let psmRowSeed = 0;
const psmRows = ref(createPsmRows());
const psmPricePoints = reactive({
  optimalPrice: "",
  acceptablePrice: "",
  acceptableRange: "",
});
const psmChart = reactive({ url: "", name: "" });
const psmChartInput = ref(null);
const uploadingPsmChart = ref(false);
const psmChartPreviewList = computed(() => (psmChart.url ? [psmChart.url] : []));
const selectedCostMarkupChannel = ref("");
const finalPricingAnalysis = ref("");
const dynamicStepAnswers = ref({});
const canRemovePsmRow = computed(() => psmRows.value.some((row) => !row.isDefault));
const canRemovePerceivedValueItem = computed(() => perceivedValueItems.value.some((row) => !row.isDefault));
const pricingSteps = computed(() => [
  { no: 1, title: labels.pricingTitle },
  { no: 2, title: labels.competitorPricingTitle },
  { no: 3, title: labels.valueBasedPricingTitle },
  { no: 4, title: labels.psmTitle },
  { no: 5, title: labels.finalPricingTitle },
]);
const currentStepTitle = computed(() => {
  if (currentStep.value === 1) return labels.pricingTitle;
  if (currentStep.value === 2) return labels.competitorPricingTitle;
  if (currentStep.value === 3) return labels.valueBasedPricingTitle;
  if (currentStep.value === 4) return labels.psmTitle;
  return labels.finalPricingTitle;
});

const taskTitle = computed(() => taskConfig.value?.taskName || "\u65b0\u4ea7\u54c1\u5b9a\u4ef7");
const taskBackground = computed(
  () =>
    taskConfig.value?.background ||
    "\u57fa\u4e8e\u6848\u4f8b\u4e2d\u7684\u5355\u53f0\u7efc\u5408\u6210\u672c\uff0c\u7ed3\u5408\u6e20\u9053\u7c7b\u578b\u548c\u54c1\u724c\u5b9a\u4f4d\uff0c\u5b8c\u6210\u4e0d\u540c\u6e20\u9053\u7684\u52a0\u6210\u5b9a\u4ef7\u3002",
);
const taskRequirement = computed(
  () =>
    taskConfig.value?.requirements ||
    "\u586b\u5199\u5355\u53f0\u7efc\u5408\u6210\u672c\uff0c\u5e76\u4e3a\u4e09\u79cd\u6e20\u9053\u586b\u5199\u5efa\u8bae\u8303\u56f4\u5185\u7684\u52a0\u6210\u7387\uff0c\u7cfb\u7edf\u81ea\u52a8\u8ba1\u7b97\u52a0\u6210\u552e\u4ef7\u3002",
);
const taskGoals = computed(() =>
  parseTrainingArray(taskConfig.value?.objectives, [
    "\u7406\u89e3\u6210\u672c\u52a0\u6210\u5b9a\u4ef7\u7684\u57fa\u672c\u903b\u8f91",
    "\u533a\u5206\u4e0d\u540c\u6e20\u9053\u7684\u52a0\u6210\u7b56\u7565",
    "\u5b8c\u6210\u4e09\u7c7b\u6e20\u9053\u7684\u552e\u4ef7\u8ba1\u7b97",
  ]),
);
const progressItems = computed(() => [{ text: "\u65b0\u4ea7\u54c1\u5b9a\u4ef7", status: "doing" }]);

const grossMarginRate = computed(() => {
  const cost = parseNumber(unitCost.value);
  const price = parseNumber(pricingDecision.value.targetPrice);
  if (cost === null || cost <= 0 || price === null || price <= 0) return null;
  return ((price - cost) / price) * 100;
});

const perceivedValueTotal = computed(() => {
  const values = perceivedValueItems.value.map((row) => parseNumber(row.perceivedValue));
  if (values.some((value) => value === null || value <= 0)) return null;
  return values.reduce((total, value) => total + value, 0);
});

const valueBasedPrice = computed(() => {
  const discountRate = parseNumber(perceivedValueDiscountRate.value);
  if (perceivedValueTotal.value === null || discountRate === null || discountRate <= 0 || discountRate > 100) return null;
  return perceivedValueTotal.value * discountRate / 100;
});

const costMarkupChannelOptions = computed(() =>
  pricingRows.value.map((row) => {
    const price = salePrice(row);
    return {
      value: row.channel,
      label: price === null ? `${row.channel}（待完成第 1 步）` : `${row.channel}（${labels.yuanSign} ${formatMoney(price)}）`,
      disabled: price === null,
    };
  }),
);

const stepFiveMethodResults = computed(() => {
  const unitCostValue = parseNumber(unitCost.value);
  const selectedCostMarkupRow = pricingRows.value.find((row) => row.channel === selectedCostMarkupChannel.value);
  const results = [
    { key: "cost-markup", methodName: "成本加成定价", measuredPrice: selectedCostMarkupRow ? salePrice(selectedCostMarkupRow) : null },
    { key: "competitive", methodName: "竞争定价", measuredPrice: parseNumber(pricingDecision.value.targetPrice) },
    { key: "value-based", methodName: "价值定价", measuredPrice: valueBasedPrice.value },
    { key: "psm", methodName: "PSM价格敏感度", measuredPrice: parseNumber(psmPricePoints.optimalPrice) },
  ];
  return results.map((result) => ({
    ...result,
    grossMarginRate: unitCostValue === null || unitCostValue <= 0 || result.measuredPrice === null || result.measuredPrice <= 0
      ? null
      : ((result.measuredPrice - unitCostValue) / result.measuredPrice) * 100,
  }));
});

function createPricingRows() {
  return DEFAULT_ROWS.map((row) => ({ ...row, markupRate: "" }));
}

function createCompetitorRows() {
  return Array.from({ length: 4 }, (_, index) => ({
    id: `competitor-${index + 1}`,
    label: `\u7ade\u54c1${index + 1}`,
    productType: "",
    brand: "",
    ecommercePrice: "",
    differentiator: "",
  }));
}

function createPricingDecision() {
  return {
    benchmarkCompetitor: "",
    targetPrice: "",
    grossMarginRate: null,
    differentiator: "",
    pricingReason: "",
  };
}

function createPerceivedValueItem(row = {}) {
  return {
    id: row.id || `perceived-value-custom-${++perceivedValueItemSeed}`,
    name: row.name ?? "",
    perceivedValue: row.perceivedValue ?? "",
    isDefault: Boolean(row.isDefault),
  };
}

function createPerceivedValueItems(savedItems = []) {
  const sourceItems = Array.isArray(savedItems) ? savedItems : [];
  const defaultItem = createPerceivedValueItem({
    ...DEFAULT_PERCEIVED_VALUE_ITEM,
    ...sourceItems.find((item) => item?.id === DEFAULT_PERCEIVED_VALUE_ITEM.id),
    isDefault: true,
  });
  const customItems = sourceItems
    .filter((item) => item && item.id !== DEFAULT_PERCEIVED_VALUE_ITEM.id)
    .map((item) => createPerceivedValueItem({ ...item, isDefault: false }));
  return [defaultItem, ...customItems];
}

function addPerceivedValueItem() {
  perceivedValueItems.value.push(createPerceivedValueItem());
}

function removePerceivedValueItem() {
  for (let index = perceivedValueItems.value.length - 1; index >= 0; index -= 1) {
    if (!perceivedValueItems.value[index].isDefault) {
      perceivedValueItems.value.splice(index, 1);
      return;
    }
  }
}

function createPsmRow(row = {}) {
  return {
    id: row.id || `psm-custom-${++psmRowSeed}`,
    priceRange: row.priceRange || "",
    isDefault: Boolean(row.isDefault),
    tooExpensive: row.tooExpensive ?? "",
    tooCheap: row.tooCheap ?? "",
    expensiveAcceptable: row.expensiveAcceptable ?? "",
    cheapValue: row.cheapValue ?? "",
  };
}

function createPsmRows(savedRows = []) {
  const sourceRows = Array.isArray(savedRows) ? savedRows : [];
  const defaultRows = DEFAULT_PSM_PRICE_RANGES.map((row) =>
    createPsmRow({
      ...row,
      ...sourceRows.find((item) => item?.id === row.id),
      isDefault: true,
    })
  );
  const customRows = sourceRows
    .filter((row) => row && !DEFAULT_PSM_PRICE_RANGES.some((item) => item.id === row.id))
    .map((row) => createPsmRow({ ...row, isDefault: false }));
  return [...defaultRows, ...customRows];
}

function addPsmRow() {
  psmRows.value.push(createPsmRow());
}

function removePsmRow() {
  for (let index = psmRows.value.length - 1; index >= 0; index -= 1) {
    if (!psmRows.value[index].isDefault) {
      psmRows.value.splice(index, 1);
      return;
    }
  }
}

function buildStepFourProgress() {
  return {
    psmRows: psmRows.value.map((row) => ({ ...row })),
    pricePoints: { ...psmPricePoints },
    chart: { ...psmChart },
  };
}

function applyStepFourProgress(progress) {
  if (!progress || typeof progress !== "object") return;
  psmRows.value = createPsmRows(progress.psmRows);
  Object.assign(
    psmPricePoints,
    { optimalPrice: "", acceptablePrice: "", acceptableRange: "" },
    progress.pricePoints || {}
  );
  Object.assign(psmChart, { url: "", name: "" }, progress.chart || {});
}

function buildStepFiveProgress() {
  return {
    selectedCostMarkupChannel: selectedCostMarkupChannel.value,
    methodResults: stepFiveMethodResults.value.map((result) => ({ ...result })),
    finalAnalysis: finalPricingAnalysis.value,
    updatedAt: new Date().toISOString(),
  };
}

function applyStepFiveProgress(progress) {
  if (!progress || typeof progress !== "object") return;
  selectedCostMarkupChannel.value = progress.selectedCostMarkupChannel ?? "";
  finalPricingAnalysis.value = progress.finalAnalysis ?? "";
}

function triggerPsmChartUpload() {
  psmChartInput.value?.click();
}

async function handlePsmChartUpload(event) {
  const file = event?.target?.files?.[0];
  if (event?.target) event.target.value = "";
  if (!file) return;

  uploadingPsmChart.value = true;
  try {
    let url = "";
    if (isStudentDemo()) {
      url = URL.createObjectURL(file);
    } else {
      const formData = new FormData();
      formData.append("file", file);
      const response = await uploadStudentTrainingFile(formData);
      url = response?.url || response?.data?.url || "";
    }
    if (!url) throw new Error(labels.psmChartUploadError);
    Object.assign(psmChart, { url, name: file.name });
    proxy?.$modal?.msgSuccess?.(labels.psmChartUploadSuccess);
  } catch (error) {
    proxy?.$modal?.msgError?.(error?.message || labels.psmChartUploadError);
  } finally {
    uploadingPsmChart.value = false;
  }
}

function removePsmChart() {
  Object.assign(psmChart, { url: "", name: "" });
}

function formatPsmCsvCell(value) {
  return `"${String(value ?? "").replace(/"/g, '""')}"`;
}

function exportPsmTable() {
  const rows = [
    [
      labels.psmPriceRange,
      labels.psmTooExpensive,
      labels.psmTooCheap,
      labels.psmExpensiveAcceptable,
      labels.psmCheapValue,
    ],
    ...psmRows.value.map((row) => [
      row.priceRange,
      row.tooExpensive,
      row.tooCheap,
      row.expensiveAcceptable,
      row.cheapValue,
    ]),
    [],
    [labels.psmOptimalPrice, psmPricePoints.optimalPrice],
    [labels.psmAcceptablePrice, psmPricePoints.acceptablePrice],
    [labels.psmAcceptableRange, psmPricePoints.acceptableRange],
  ];
  const blob = new Blob(
    [`\ufeff${rows.map((row) => row.map(formatPsmCsvCell).join(",")).join("\n")}`],
    { type: "text/csv;charset=utf-8;" }
  );
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = "PSM价格敏感度测试.csv";
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

function downloadPsmGuide() {
  const content = [
    "PSM价格敏感度测试实训操作说明",
    "1. 根据案例资料填写六个固定价格区间的四类累计占比：太贵不买、太便宜不买、偏高可接受、便宜划算。",
    "2. 可增加自定义价格区间；固定的六个价格区间必须保留。",
    "3. 点击“导出”下载当前数据，在 Excel 中绘制 PSM 价格数据折线图。",
    "4. 上传绘制完成的折线图，上传后可点击缩略图预览。",
    "5. 填写最优价格点、可接受价格点和可接受价格范围后保存。",
  ].join("\n");
  const blob = new Blob([`\ufeff${content}`], { type: "text/plain;charset=utf-8;" });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = "PSM价格敏感度测试实训操作说明.txt";
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
}

function validateStepFour() {
  for (const [index, row] of psmRows.value.entries()) {
    if (isBlank(row.id) || isBlank(row.priceRange)) {
      return interpolate(labels.psmRowRequired, { index: index + 1 });
    }
    for (const [field, label] of [["tooExpensive", "太贵不买"], ["tooCheap", "太便宜不买"], ["expensiveAcceptable", "偏高可接受"], ["cheapValue", "便宜划算"]]) {
      const value = parseNumber(row[field]);
      if (value === null || value < 0 || value > 100) {
        return interpolate(labels.psmPercentageRange, { index: index + 1, label });
      }
    }
  }
  const optimalPrice = parseNumber(psmPricePoints.optimalPrice);
  const acceptablePrice = parseNumber(psmPricePoints.acceptablePrice);
  if (optimalPrice === null || optimalPrice <= 0 || acceptablePrice === null || acceptablePrice <= 0 || isBlank(psmPricePoints.acceptableRange)) {
    return labels.psmPricePointsRequired;
  }
  return "";
}

async function saveStepFour() {
  if (saving.value) return;
  const message = validateStepFour();
  validationMessage.value = message;
  if (message) {
    proxy?.$modal?.msgWarning?.(message);
    return;
  }
  saving.value = true;
  try {
    const validationResponse = await validateProductPricingStepFour({
      psmRows: psmRows.value.map((row) => ({
        id: row.id,
        priceRange: row.priceRange,
        tooExpensive: parseNumber(row.tooExpensive),
        tooCheap: parseNumber(row.tooCheap),
        expensiveAcceptable: parseNumber(row.expensiveAcceptable),
        cheapValue: parseNumber(row.cheapValue),
      })),
      pricePoints: {
        optimalPrice: parseNumber(psmPricePoints.optimalPrice),
        acceptablePrice: parseNumber(psmPricePoints.acceptablePrice),
        acceptableRange: psmPricePoints.acceptableRange,
      },
    });
    const validated = validationResponse?.data || validationResponse || {};
    if (Array.isArray(validated.psmRows)) {
      psmRows.value = createPsmRows(validated.psmRows);
    }
    if (validated.pricePoints) {
      Object.assign(psmPricePoints, validated.pricePoints);
    }
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 4,
      step4Answer: JSON.stringify(buildStepFourProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.psmSaveSuccess);
  } finally {
    saving.value = false;
  }
}

function validateStepFive() {
  if (isBlank(selectedCostMarkupChannel.value)) return labels.finalPricingChannelRequired;
  if (stepFiveMethodResults.value.some((result) => result.measuredPrice === null || result.measuredPrice <= 0 || result.grossMarginRate === null)) {
    return labels.finalPricingPrerequisiteRequired;
  }
  if (isBlank(finalPricingAnalysis.value)) return labels.finalPricingAnalysisRequired;
  return "";
}

async function saveStepFive() {
  if (saving.value) return;
  const message = validateStepFive();
  validationMessage.value = message;
  if (message) {
    proxy?.$modal?.msgWarning?.(message);
    return;
  }
  saving.value = true;
  try {
    const validationResponse = await validateProductPricingStepFive({
      selectedCostMarkupChannel: selectedCostMarkupChannel.value,
      methodResults: stepFiveMethodResults.value.map((result) => ({
        key: result.key,
        methodName: result.methodName,
        measuredPrice: result.measuredPrice,
        grossMarginRate: result.grossMarginRate,
      })),
      finalAnalysis: finalPricingAnalysis.value,
    });
    const validated = validationResponse?.data || validationResponse || {};
    selectedCostMarkupChannel.value = validated.selectedCostMarkupChannel ?? selectedCostMarkupChannel.value;
    finalPricingAnalysis.value = validated.finalAnalysis ?? finalPricingAnalysis.value;
    dynamicStepAnswers.value = { ...dynamicStepAnswers.value, step5: buildStepFiveProgress() };
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 5,
      dynamicStepAnswers: JSON.stringify(dynamicStepAnswers.value),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.finalPricingSaveSuccess);
  } finally {
    saving.value = false;
  }
}

function parseNumber(value) {
  if (value === null || value === undefined || String(value).trim() === "") {
    return null;
  }
  const normalized = String(value).trim().replace(/[%\uff0c,]/g, "");
  const number = Number(normalized);
  return Number.isFinite(number) ? number : null;
}

function salePrice(row) {
  const cost = parseNumber(unitCost.value);
  const rate = parseNumber(row.markupRate);
  if (cost === null || cost < 0 || rate === null || rate < 0) {
    return null;
  }
  return cost * (1 + rate / 100);
}

function formatMoney(value) {
  return Number(value).toFixed(2);
}

function ratePlaceholder(row) {
  return `${row.minRate}%-${row.maxRate}%`;
}

function rateHint(row) {
  return `${labels.rangePrefix}${row.minRate}%-${row.maxRate}%`;
}

function clearValidation() {
  validationMessage.value = "";
}

function interpolate(template, values) {
  return Object.entries(values).reduce(
    (result, [key, value]) => result.replace(`{${key}}`, value),
    template,
  );
}

function validateStepOne() {
  const cost = parseNumber(unitCost.value);
  if (cost === null || cost <= 0) {
    return labels.costRequired;
  }
  for (const row of pricingRows.value) {
    const rate = parseNumber(row.markupRate);
    if (rate === null) {
      return interpolate(labels.rateRequired, { channel: row.channel });
    }
    if (rate < row.minRate || rate > row.maxRate) {
      return interpolate(labels.rateRange, {
        channel: row.channel,
        min: row.minRate,
        max: row.maxRate,
      });
    }
  }
  return "";
}

function buildStepOneProgress() {
  return {
    version: 1,
    unitCost: unitCost.value,
    pricingRows: pricingRows.value.map((row) => ({
      id: row.id,
      markupRate: row.markupRate,
    })),
    updatedAt: new Date().toISOString(),
  };
}

function applyStepOneProgress(progress) {
  if (!progress || typeof progress !== "object") {
    return;
  }
  unitCost.value = progress.unitCost ?? "";
  const savedRows = new Map(
    Array.isArray(progress.pricingRows)
      ? progress.pricingRows.map((row) => [row?.id, row])
      : [],
  );
  pricingRows.value = DEFAULT_ROWS.map((row) => ({
    ...row,
    markupRate: savedRows.get(row.id)?.markupRate ?? "",
  }));
}

function buildStepTwoProgress() {
  return {
    version: 1,
    competitorRows: competitorRows.value.map((row) => ({
      id: row.id,
      productType: row.productType,
      brand: row.brand,
      ecommercePrice: row.ecommercePrice,
      differentiator: row.differentiator,
    })),
    pricingDecision: { ...pricingDecision.value },
    updatedAt: new Date().toISOString(),
  };
}

function applyStepTwoProgress(progress) {
  if (!progress || typeof progress !== "object") {
    return;
  }
  const savedRows = new Map(
    Array.isArray(progress.competitorRows)
      ? progress.competitorRows.map((row) => [row?.id, row])
      : [],
  );
  competitorRows.value = createCompetitorRows().map((row) => {
    const saved = savedRows.get(row.id) || {};
    return {
      ...row,
      productType: saved.productType ?? "",
      brand: saved.brand ?? "",
      ecommercePrice: saved.ecommercePrice ?? "",
      differentiator: saved.differentiator ?? "",
    };
  });
  pricingDecision.value = {
    ...createPricingDecision(),
    ...(progress.pricingDecision || {}),
  };
}

function buildStepThreeProgress() {
  return {
    version: 1,
    valueItems: perceivedValueItems.value.map((row) => ({
      id: row.id,
      name: row.name,
      perceivedValue: row.perceivedValue,
    })),
    discountRate: perceivedValueDiscountRate.value,
    perceivedValueTotal: perceivedValueTotal.value,
    valueBasedPrice: valueBasedPrice.value,
    updatedAt: new Date().toISOString(),
  };
}

function applyStepThreeProgress(progress) {
  if (!progress || typeof progress !== "object") return;
  perceivedValueItems.value = createPerceivedValueItems(progress.valueItems);
  perceivedValueDiscountRate.value = progress.discountRate ?? "";
}

function isBlank(value) {
  return !String(value ?? "").trim();
}

function competitorOptionLabel(row, index) {
  const productType = String(row?.productType || "").trim();
  const brand = String(row?.brand || "").trim();
  return productType ? `竞品${index + 1}：${productType}${brand ? `（${brand}）` : ""}` : `竞品${index + 1}：请先填写产品类型`;
}

function validateStepTwo() {
  const cost = parseNumber(unitCost.value);
  if (cost === null || cost <= 0) {
    return labels.costRequiredForStepTwo;
  }
  for (const [index, row] of competitorRows.value.entries()) {
    if (isBlank(row.productType) || isBlank(row.brand) || isBlank(row.differentiator)) {
      return interpolate(labels.competitorRequired, { index: index + 1 });
    }
    const ecommercePrice = parseNumber(row.ecommercePrice);
    if (ecommercePrice === null || ecommercePrice <= 0) {
      return interpolate(labels.competitorPriceRequired, { index: index + 1 });
    }
  }
  if (isBlank(pricingDecision.value.benchmarkCompetitor)) {
    return labels.benchmarkRequired;
  }
  const targetPrice = parseNumber(pricingDecision.value.targetPrice);
  if (targetPrice === null || targetPrice <= 0) {
    return labels.targetPriceRequired;
  }
  if (isBlank(pricingDecision.value.differentiator)) {
    return labels.differentiatorRequired;
  }
  if (isBlank(pricingDecision.value.pricingReason)) {
    return labels.pricingReasonRequired;
  }
  return "";
}

function validateStepThree() {
  for (const [index, row] of perceivedValueItems.value.entries()) {
    if (isBlank(row.name)) {
      return interpolate(labels.perceivedValueItemRequired, { index: index + 1 });
    }
    const perceivedValue = parseNumber(row.perceivedValue);
    if (perceivedValue === null || perceivedValue <= 0) {
      return interpolate(labels.perceivedValueAmountRequired, { index: index + 1 });
    }
  }
  const discountRate = parseNumber(perceivedValueDiscountRate.value);
  if (discountRate === null || discountRate <= 0 || discountRate > 100) {
    return labels.discountRateRequired;
  }
  return "";
}

function parseSavedJson(raw) {
  if (!raw) {
    return null;
  }
  try {
    return typeof raw === "string" ? JSON.parse(raw) : raw;
  } catch (error) {
    return null;
  }
}

function switchStep(step) {
  currentStep.value = step;
  clearValidation();
}

async function saveStepOne() {
  if (saving.value) {
    return;
  }
  const message = validateStepOne();
  validationMessage.value = message;
  if (message) {
    proxy?.$modal?.msgWarning?.(message);
    return;
  }

  saving.value = true;
  try {
    const validationResponse = await validateProductPricingStepOne({
      unitCost: parseNumber(unitCost.value),
      pricingRows: pricingRows.value.map((row) => ({ id: row.id, markupRate: parseNumber(row.markupRate) })),
    });
    const validated = validationResponse?.data || validationResponse || {};
    if (validated.unitCost !== undefined && validated.unitCost !== null) {
      unitCost.value = String(validated.unitCost);
    }
    const validatedRows = new Map((validated.pricingRows || []).map((row) => [row?.id, row]));
    pricingRows.value = pricingRows.value.map((row) => ({
      ...row,
      markupRate: validatedRows.get(row.id)?.markupRate ?? row.markupRate,
    }));
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 2,
      step1Answer: JSON.stringify(buildStepOneProgress()),
    });
    hasSavedAnswer.value = true;
    currentStep.value = 2;
    proxy?.$modal?.msgSuccess?.(labels.stepOneSaveSuccess);
  } finally {
    saving.value = false;
  }
}

async function resetStepOne() {
  if (saving.value) {
    return;
  }
  unitCost.value = "";
  pricingRows.value = createPricingRows();
  validationMessage.value = "";

  saving.value = true;
  try {
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "RESET",
      currentStep: 1,
      step1Answer: JSON.stringify(buildStepOneProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.resetSuccess);
  } finally {
    saving.value = false;
  }
}

async function saveStepTwo() {
  if (saving.value) {
    return;
  }
  const message = validateStepTwo();
  validationMessage.value = message;
  if (message) {
    proxy?.$modal?.msgWarning?.(message);
    return;
  }

  saving.value = true;
  try {
    const validationResponse = await validateProductPricingStepTwo({
      unitCost: parseNumber(unitCost.value),
      competitorRows: competitorRows.value.map((row) => ({ id: row.id, productType: row.productType, brand: row.brand, ecommercePrice: parseNumber(row.ecommercePrice), differentiator: row.differentiator })),
      decision: { ...pricingDecision.value, targetPrice: parseNumber(pricingDecision.value.targetPrice) },
    });
    const validated = validationResponse?.data || validationResponse || {};
    const validatedRows = new Map((validated.competitorRows || []).map((row) => [row?.id, row]));
    competitorRows.value = competitorRows.value.map((row) => ({ ...row, ...(validatedRows.get(row.id) || {}) }));
    pricingDecision.value = { ...pricingDecision.value, ...(validated.decision || {}) };
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 2,
      step2Answer: JSON.stringify(buildStepTwoProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.stepTwoSaveSuccess);
  } finally {
    saving.value = false;
  }
}

async function resetStepTwo() {
  if (saving.value) {
    return;
  }
  competitorRows.value = createCompetitorRows();
  pricingDecision.value = createPricingDecision();
  validationMessage.value = "";

  saving.value = true;
  try {
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 2,
      step2Answer: JSON.stringify(buildStepTwoProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.stepTwoResetSuccess);
  } finally {
    saving.value = false;
  }
}

async function saveStepThree() {
  if (saving.value) return;
  const message = validateStepThree();
  validationMessage.value = message;
  if (message) {
    proxy?.$modal?.msgWarning?.(message);
    return;
  }

  saving.value = true;
  try {
    const validationResponse = await validateProductPricingStepThree({
      valueItems: perceivedValueItems.value.map((row) => ({
        id: row.id,
        name: row.name,
        perceivedValue: parseNumber(row.perceivedValue),
      })),
      discountRate: parseNumber(perceivedValueDiscountRate.value),
    });
    const validated = validationResponse?.data || validationResponse || {};
    if (Array.isArray(validated.valueItems)) {
      perceivedValueItems.value = createPerceivedValueItems(validated.valueItems);
    }
    if (validated.discountRate !== undefined && validated.discountRate !== null) {
      perceivedValueDiscountRate.value = String(validated.discountRate);
    }
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 3,
      step3Answer: JSON.stringify(buildStepThreeProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.stepThreeSaveSuccess);
  } finally {
    saving.value = false;
  }
}

async function resetStepThree() {
  if (saving.value) return;
  perceivedValueItems.value = createPerceivedValueItems();
  perceivedValueDiscountRate.value = "";
  validationMessage.value = "";

  saving.value = true;
  try {
    await saveStudentTrainingAnswer(TASK_KEY, {
      saveAction: "SAVE",
      currentStep: 3,
      step3Answer: JSON.stringify(buildStepThreeProgress()),
    });
    hasSavedAnswer.value = true;
    proxy?.$modal?.msgSuccess?.(labels.stepThreeResetSuccess);
  } finally {
    saving.value = false;
  }
}

async function loadTaskConfig() {
  try {
    const response = await getTrainingTaskByKey(TASK_KEY);
    taskConfig.value = response?.data || response || null;
  } catch (error) {
    taskConfig.value = null;
  }
}

async function loadSavedProgress() {
  try {
    const response = await getStudentTrainingAnswer(TASK_KEY);
    const answer = response?.data || response || {};
    const stepOneProgress = parseSavedJson(answer?.step1Answer);
    const stepTwoProgress = parseSavedJson(answer?.step2Answer);
    const stepThreeProgress = parseSavedJson(answer?.step3Answer);
    const stepFourProgress = parseSavedJson(answer?.step4Answer);
    dynamicStepAnswers.value = parseSavedJson(answer?.dynamicStepAnswers) || {};
    const stepFiveProgress = dynamicStepAnswers.value?.step5;
    hasSavedAnswer.value = Boolean(stepOneProgress || stepTwoProgress || stepThreeProgress || stepFourProgress || stepFiveProgress);
    if (stepOneProgress) {
      applyStepOneProgress(stepOneProgress);
    }
    if (stepTwoProgress) {
      applyStepTwoProgress(stepTwoProgress);
      currentStep.value = 2;
    }
    if (stepThreeProgress) {
      applyStepThreeProgress(stepThreeProgress);
      currentStep.value = 3;
    }
    if (stepFourProgress) {
      applyStepFourProgress(stepFourProgress);
      currentStep.value = 4;
    }
    if (stepFiveProgress) {
      applyStepFiveProgress(stepFiveProgress);
      currentStep.value = 5;
    }
  } catch (error) {
    hasSavedAnswer.value = false;
  }
}

onMounted(async () => {
  await Promise.all([loadTaskConfig(), loadSavedProgress()]);
});
</script>

<style scoped lang="scss">
.price-page {
  --shell-bg: #061923;
  --surface: rgba(8, 35, 48, 0.9);
  --surface-soft: rgba(13, 51, 67, 0.7);
  --line: rgba(91, 197, 231, 0.25);
  --line-strong: rgba(87, 211, 247, 0.55);
  --ink: #edfaff;
  --muted: #a3c4d0;
  --accent: #24c6f2;
  --accent-deep: #1184bb;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 330px;
  gap: 24px;
  min-height: 100%;
  padding: 26px;
  color: var(--ink);
  background:
    radial-gradient(circle at 10% 0%, rgba(24, 164, 213, 0.18), transparent 31rem),
    linear-gradient(135deg, #061923 0%, #082633 46%, #061923 100%);

  .training-core {
    min-width: 0;
  }

  .task-header {
    margin-bottom: 18px;

    .task-kicker,
    h1 {
      margin: 0;
    }

    .task-kicker,
    .section-eyebrow {
      color: #66d9fb;
      font-size: 12px;
      font-weight: 700;
      letter-spacing: 0.16em;
    }

    h1 {
      margin-top: 6px;
      font-size: 30px;
      line-height: 1.25;
    }
  }

  .price-workbench {
    overflow: hidden;
    margin-top: 18px;
    border: 1px solid var(--line-strong);
    border-radius: 18px;
    background: var(--surface);
    box-shadow: 0 20px 45px rgba(0, 0, 0, 0.2);
  }

  .step-indicator {
    display: flex;
    align-items: center;
    width: calc(100% - 48px);
    margin: 18px 24px 0;
    padding: 12px 14px;
    overflow-x: auto;
    border: 1px solid rgba(0, 180, 255, 0.3);
    border-radius: 14px;
    background: linear-gradient(110deg, rgba(4, 39, 58, 0.9), rgba(2, 20, 33, 0.94));
    box-shadow: inset 0 1px 0 rgba(169, 229, 255, 0.08);
  }

  .step-tab {
    display: flex;
    flex: 1 0 136px;
    align-items: center;
    min-width: 0;
    min-height: 52px;
    padding: 7px 9px;
    color: #9ab3d0;
    cursor: pointer;
    border: 1px solid rgba(68, 141, 177, 0.35);
    border-radius: 8px;
    background: rgba(3, 27, 43, 0.52);
    transition: transform 0.2s, color 0.2s, background 0.2s, border-color 0.2s, box-shadow 0.2s;

    &:hover,
    &:focus-visible {
      color: #fff;
      border-color: rgba(89, 223, 255, 0.82);
      outline: none;
      background: rgba(8, 92, 131, 0.52);
      box-shadow: 0 8px 18px rgba(0, 0, 0, 0.2);
      transform: translateY(-2px);
    }

    &.active {
      color: #fff;
      border-color: rgba(89, 226, 255, 0.85);
      background: linear-gradient(115deg, rgba(4, 109, 155, 0.78), rgba(5, 62, 102, 0.64));
      box-shadow: 0 0 18px rgba(24, 183, 235, 0.22), inset 0 1px 0 rgba(176, 246, 255, 0.2);
    }

    &.complete {
      color: #dffbff;
    }
  }

  .step-marker {
    display: inline-flex;
    flex: 0 0 30px;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    color: #a6c4d9;
    border: 1px solid rgba(101, 176, 208, 0.6);
    border-radius: 50%;
    background: rgba(3, 25, 40, 0.8);
    box-shadow: inset 0 0 12px rgba(38, 174, 233, 0.08);
    font-size: 11px;
    font-weight: 900;
    letter-spacing: 0.04em;
  }

  .step-copy {
    display: grid;
    min-width: 0;
    gap: 3px;
    margin-left: 8px;

    strong {
      display: -webkit-box;
      overflow: hidden;
      color: inherit;
      font-size: 13px;
      line-height: 1.25;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
    }

    span {
      color: #66b6d4;
      font-size: 10px;
      font-weight: 800;
      letter-spacing: 0.06em;
    }
  }

  .step-tab.active .step-marker {
    color: #fff;
    border-color: #72e8ff;
    background: linear-gradient(135deg, #08b7e8, #1375d0);
    box-shadow: 0 0 0 5px rgba(25, 179, 237, 0.14), 0 0 18px rgba(46, 208, 255, 0.58);
  }

  .step-tab.complete .step-marker {
    color: #062b37;
    border-color: rgba(84, 230, 221, 0.85);
    background: #6ef0e1;
    box-shadow: 0 0 12px rgba(79, 231, 218, 0.35);
  }

  .step-connector {
    position: relative;
    flex: 0 1 40px;
    min-width: 18px;
    height: 2px;
    margin: 0 8px;
    color: transparent;
    background: rgba(98, 169, 201, 0.34);

    &::after {
      position: absolute;
      top: 50%;
      right: -1px;
      width: 6px;
      height: 6px;
      border-top: 1px solid #73b1ce;
      border-right: 1px solid #73b1ce;
      content: "";
      transform: translateY(-50%) rotate(45deg);
    }

    &.complete {
      background: linear-gradient(90deg, #6ef0e1, #1cb8e9);
      box-shadow: 0 0 9px rgba(53, 218, 235, 0.46);

      &::after {
        border-color: #63e6f1;
      }
    }
  }

  .primary-button,
  .secondary-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    padding: 0 14px;
    color: #e9fbff;
    cursor: pointer;
    border: 1px solid rgba(99, 217, 255, 0.55);
    border-radius: 8px;
    font: inherit;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    transition: transform 0.18s, border-color 0.18s, background 0.18s, opacity 0.18s;

    &:hover:not(:disabled),
    &:focus-visible:not(:disabled) {
      border-color: #74ebff;
      outline: none;
      transform: translateY(-1px);
    }

    &:disabled {
      cursor: not-allowed;
      opacity: 0.45;
    }
  }

  .primary-button {
    background: linear-gradient(135deg, #08a5d6, #147bb9);
  }

  .secondary-button {
    background: rgba(17, 126, 178, 0.36);
  }

  .toolbar-actions {
    display: flex;
    flex: none;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px;
  }

  .workbench-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 24px 26px 18px;
    border-bottom: 1px solid var(--line);

    h2 {
      margin: 4px 0 0;
      font-size: 24px;
    }
  }

  .intro-copy {
    margin: 20px 26px;
    color: var(--muted);
    line-height: 1.75;
  }

  .unit-cost-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    margin: 0 26px 22px;
    padding: 18px 20px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface-soft);
  }

  .cost-panel-copy {
    display: flex;
    align-items: flex-start;
    gap: 12px;

    p {
      margin: 0 0 4px;
      font-size: 16px;
      font-weight: 700;
    }

    small {
      color: var(--muted);
      line-height: 1.55;
    }
  }

  .cost-dot {
    width: 10px;
    height: 10px;
    margin-top: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 12px var(--accent);
  }

  .unit-cost-input {
    width: 270px;
  }

  .pricing-table-wrap {
    overflow-x: auto;
    margin: 0 26px;
    border: 1px solid var(--line);
    border-radius: 14px;
  }

  .pricing-table {
    width: 100%;
    min-width: 720px;
    border-collapse: collapse;

    th,
    td {
      padding: 16px;
      border-bottom: 1px solid var(--line);
      text-align: left;
    }

    th {
      color: #ddf8ff;
      font-size: 14px;
      background: rgba(25, 122, 157, 0.46);
    }

    tbody tr:last-child td {
      border-bottom: 0;
    }

    th:first-child,
    td:first-child {
      width: 84px;
      text-align: center;
    }
  }

  .competitor-section {
    margin-bottom: 24px;
  }

  .subsection-title {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 26px 12px;

    span {
      display: grid;
      width: 22px;
      height: 22px;
      place-items: center;
      color: #7de2ff;
      font-size: 12px;
      font-weight: 700;
      background: rgba(117, 226, 255, 0.18);
      border-radius: 50%;
    }

    h3 {
      margin: 0;
      font-size: 16px;
    }
  }

  .competitor-table {
    min-width: 900px;
  }

  .decision-table {
    min-width: 1160px;
  }

  .competitor-table th:first-child,
  .competitor-table td:first-child,
  .decision-table th:first-child,
  .decision-table td:first-child {
    width: auto;
    text-align: left;
  }

  .competitor-table td,
  .decision-table td {
    vertical-align: top;
  }

  .serial-cell {
    color: #7de2ff;
    font-weight: 700;
  }

  .channel-cell {
    color: #f2fcff;
    font-weight: 600;
  }

  .markup-input {
    width: 190px;
  }

  .range-hint {
    display: block;
    margin-top: 6px;
    color: var(--muted);
    font-size: 12px;
  }

  .sale-price-cell {
    color: #7de2ff;
    font-size: 17px;
    font-weight: 700;
  }

  .empty-price {
    color: #7998a3;
    font-size: 14px;
    font-weight: 400;
  }

  .validation-message {
    margin: 14px 26px 0;
    color: #ff9e9e;
    line-height: 1.5;
  }

  .value-pricing-section {
    display: grid;
    gap: 20px;
  }

  .value-pricing-toolbar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 18px;

    p {
      max-width: 760px;
      margin: 0;
      color: var(--muted);
      line-height: 1.7;
    }
  }

  .perceived-value-table {
    min-width: 620px;

    th,
    td {
      width: 50%;
    }

    .total-row td {
      color: #dffaff;
      font-weight: 700;
      background: rgba(26, 167, 211, 0.12);
    }
  }

  .value-pricing-result {
    display: grid;
    grid-template-columns: minmax(230px, 0.8fr) minmax(220px, 0.7fr) minmax(290px, 1fr);
    align-items: center;
    gap: 20px;
    padding: 20px 24px;
    background: rgba(5, 35, 48, 0.72);
    border: 1px solid var(--line);
    border-radius: 12px;

    label {
      display: grid;
      gap: 8px;
      color: #dffaff;
      font-weight: 600;
    }

    p {
      margin: 0;
      color: var(--muted);
      line-height: 1.6;
    }
  }

  .value-price-output {
    display: grid;
    gap: 7px;
    color: #dffaff;
    font-weight: 600;

    strong {
      color: #7de2ff;
      font-size: 23px;
    }

    em {
      color: #7998a3;
      font-size: 14px;
      font-style: normal;
      font-weight: 400;
    }
  }

  .workbench-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    margin-top: 22px;
    padding: 18px 26px;
    border-top: 1px solid var(--line);
    background: rgba(2, 19, 28, 0.42);

    p {
      margin: 0;
      color: var(--muted);
      font-size: 13px;
      line-height: 1.65;
    }
  }

  .footer-actions {
    display: flex;
    flex: none;
    gap: 10px;
  }

  :deep(.el-input__wrapper) {
    background: rgba(2, 19, 28, 0.7);
    box-shadow: 0 0 0 1px rgba(94, 213, 245, 0.3) inset;
  }

  :deep(.el-input__inner) {
    color: #fff;
  }

  :deep(.el-textarea__inner) {
    color: #fff;
    background: rgba(2, 19, 28, 0.7);
    box-shadow: 0 0 0 1px rgba(94, 213, 245, 0.3) inset;
  }

  :deep(.el-input-group__append) {
    color: #91dff5;
    border-left-color: rgba(94, 213, 245, 0.3);
    background: rgba(16, 98, 130, 0.4);
    box-shadow: none;
  }

  .final-pricing-section {
    display: grid;
    gap: 22px;
    padding-bottom: 26px;
  }

  .final-pricing-intro {
    margin-bottom: 0;
  }

  .final-pricing-channel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    margin: 0 26px;
    padding: 16px 18px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface-soft);

    h3,
    p {
      margin: 0;
    }

    h3 {
      color: #e5f9ff;
      font-size: 16px;
    }

    p {
      margin-top: 6px;
      color: var(--muted);
      font-size: 13px;
    }

    :deep(.el-select) {
      width: min(360px, 100%);
    }
  }

  .final-pricing-table {
    min-width: 680px;

    th:first-child,
    td:first-child {
      width: auto;
      text-align: left;
    }

    td:nth-child(2),
    td:nth-child(3) {
      color: #7de2ff;
      font-weight: 700;
    }
  }

  .final-analysis-section {
    margin: 0 26px;

    h3 {
      margin: 0 0 12px;
      color: #e5f9ff;
      font-size: 17px;
    }
  }

  @media (max-width: 1440px) {
    grid-template-columns: 1fr;
  }

  @media (max-width: 640px) {
    padding: 14px;

    .workbench-head,
    .unit-cost-panel,
    .workbench-footer,
    .value-pricing-toolbar,
    .final-pricing-channel {
      align-items: stretch;
      flex-direction: column;
    }

    .value-pricing-result {
      grid-template-columns: 1fr;
    }

    .unit-cost-input {
      width: 100%;
    }

    .footer-actions {
      justify-content: flex-end;
    }
  }
}
/* PSM dark theme */
.price-page {
  .psm-section {
    display: grid;
    gap: 20px;
    padding: 0 26px 26px;
  }

  .psm-toolbar,
  .psm-chart-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
  }

  .psm-toolbar p,
  .psm-chart-head p,
  .psm-guide-row p {
    margin: 0;
    color: var(--muted);
    line-height: 1.7;
  }

  .psm-subtitle {
    margin-top: 8px !important;
    color: #dffaff !important;
    font-weight: 700;
  }

  .table-scroll {
    overflow-x: auto;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: rgba(2, 19, 28, 0.42);
  }

  .psm-table {
    width: 100%;
    min-width: 980px;
    border-collapse: collapse;

    th,
    td {
      padding: 12px;
      border-bottom: 1px solid var(--line);
      text-align: left;
    }

    th {
      color: #ddf8ff;
      font-size: 14px;
      background: rgba(25, 122, 157, 0.46);
    }

    td {
      min-width: 150px;
      color: #dffaff;
    }

    td:first-child {
      min-width: 120px;
      font-weight: 700;
    }

    tbody tr:last-child td {
      border-bottom: 0;
    }
  }

  .psm-table input,
  .psm-point-grid input {
    box-sizing: border-box;
    width: 100%;
    min-height: 38px;
    padding: 8px 10px;
    color: #edfaff;
    background: rgba(2, 19, 28, 0.7);
    border: 1px solid rgba(94, 213, 245, 0.3);
    border-radius: 7px;
    outline: none;

    &:focus {
      border-color: #63e5ff;
      box-shadow: 0 0 0 3px rgba(50, 204, 241, 0.12);
    }
  }

  .psm-point-section,
  .psm-chart-section {
    padding: 20px;
    background: rgba(5, 35, 48, 0.72);
    border: 1px solid var(--line);
    border-radius: 12px;
  }

  .psm-point-section h3,
  .psm-chart-section h3 {
    margin: 0 0 14px;
    color: #e5f9ff;
    font-size: 17px;
  }

  .psm-point-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
  }

  .psm-point-grid label {
    display: grid;
    gap: 8px;
    color: #dffaff;
    font-weight: 600;
  }

  .file-picker {
    display: none;
  }

  .psm-chart-preview {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px;
    margin-top: 16px;
    background: rgba(2, 19, 28, 0.7);
    border: 1px solid var(--line);
    border-radius: 10px;
  }

  .psm-chart-image {
    width: 148px;
    height: 92px;
    overflow: hidden;
    border-radius: 8px;
    cursor: zoom-in;
  }

  .psm-chart-preview p {
    margin: 0 0 7px;
    color: #dffaff;
    word-break: break-all;
  }

  .psm-guide-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding-top: 16px;
    margin-top: 16px;
    border-top: 1px solid var(--line);
  }

  .step-footer {
    display: flex;
    justify-content: flex-end;
  }

  .text-button {
    padding: 0;
    color: #63e5ff;
    cursor: pointer;
    background: transparent;
    border: 0;
  }

  @media (max-width: 768px) {
    .psm-section {
      padding: 0 14px 18px;
    }

    .psm-toolbar,
    .psm-chart-head,
    .psm-guide-row {
      align-items: stretch;
      flex-direction: column;
    }

    .toolbar-actions {
      justify-content: flex-start;
    }

    .psm-point-grid {
      grid-template-columns: 1fr;
    }
  }
}
</style>
