<template>
  <div class="app-container">
    <div class="page-header">
      <div>
        <h2><i class="el-icon-data-analysis"></i> 生命周期分析</h2>
        <p class="page-desc">支持在 SKU 和 SPU 两个维度查看生命周期分析结果</p>
      </div>
      <div class="analysis-switch">
        <button
          v-for="item in analysisTabs"
          :key="item.value"
          type="button"
          class="switch-btn"
          :class="{ active: activeView === item.value }"
          @click="switchAnalysis(item.value)"
        >
          {{ item.label }}
        </button>
      </div>
    </div>

    <div class="upload-toolbar">
      <div class="toolbar-left">
        <el-upload
          ref="toolbarUpload"
          class="toolbar-upload"
          :limit="1"
          accept=".xlsx,.xls,.csv"
          :http-request="customUpload"
          :disabled="upload.isUploading"
          :on-change="handleFileChange"
          :before-upload="beforeUpload"
          :auto-upload="false"
          :show-file-list="false"
        >
          <el-button plain>上传文件</el-button>
        </el-upload>
        <el-button :loading="upload.isUploading" type="primary" @click="submitUpload">开始分析</el-button>
        <el-button type="success" :disabled="!hasResults" @click="exportResults">导出分析</el-button>
      </div>
      <div class="toolbar-status" v-if="upload.fileName">已上传：{{ upload.fileName }}</div>
      <div class="toolbar-status" v-else-if="upload.pendingFileName">已选择：{{ upload.pendingFileName }}</div>
      <div class="toolbar-status muted" v-else>未上传</div>
    </div>

    <template v-if="hasResults && detail">
      <div class="bg-white rounded-xl p-6 mb-20 shadow-sm">
        <div class="flex flex-wrap items-center justify-between gap-4 mb-6">
          <div class="flex items-center gap-3">
            <label class="text-sm font-medium text-gray-700">选择{{ currentConfig.entityName }}:</label>
            <select
              v-model="selectedValue"
              class="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent shadow-sm entity-select"
            >
              <option v-for="item in entityOptions" :key="item" :value="item">{{ item }}</option>
            </select>
          </div>

          <div class="flex items-center gap-2">
            <span class="text-sm font-medium text-gray-700">当前阶段:</span>
            <span class="px-3 py-1 bg-gradient-to-r from-yellow-100 to-amber-100 text-yellow-800 rounded-full text-sm font-medium flex items-center gap-2">
              {{ currentStage }}
            </span>
          </div>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
          <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
            <p class="text-xs text-gray-500 uppercase tracking-wide">{{ currentConfig.entityDisplayLabel }}</p>
            <p class="text-lg font-medium text-gray-800 truncate">{{ selectedValue }}</p>
          </div>
          <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
            <p class="text-xs text-gray-500 uppercase tracking-wide">生命周期完整性</p>
            <p class="text-lg font-medium" :class="detail && detail.is_complete ? 'text-green-700' : 'text-orange-600'">
              {{ detail && detail.is_complete ? '完整' : '不完整' }}
              <span v-if="detail && detail.completeness_score != null" class="ml-2 text-sm text-gray-500">
                (得分: {{ detail.completeness_score }}/100)
              </span>
            </p>
          </div>
          <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
            <p class="text-xs text-gray-500 uppercase tracking-wide">总销售额</p>
            <p class="text-lg font-medium text-gray-800">{{ formatCurrency(totalRevenue) }}</p>
          </div>
          <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
            <p class="text-xs text-gray-500 uppercase tracking-wide">总销量</p>
            <p class="text-lg font-medium text-gray-800">{{ totalQty }}</p>
          </div>
        </div>
      </div>

      <div class="bg-white rounded-xl p-6 mb-20 shadow-sm">
        <div class="flex justify-between items-center mb-6">
          <h3 class="text-lg font-semibold text-gray-800">{{ currentConfig.entityName }}生命周期趋势图</h3>
          <div class="flex items-center gap-3">
            <div class="flex items-center gap-2">
              <span class="w-3 h-3 rounded-full bg-blue-500"></span>
              <span class="text-sm text-gray-600">销售额</span>
            </div>
            <div class="flex items-center gap-2">
              <span class="w-3 h-3 rounded-full bg-gray-500"></span>
              <span class="text-sm text-gray-600">销量</span>
            </div>
          </div>
        </div>
        <div class="h-96">
          <canvas ref="lifeCycleTrendRef"></canvas>
        </div>
        <div class="mt-4 space-y-2">
          <div v-if="detail && detail.is_complete" class="text-sm">
            <span :class="hasFourStages ? 'text-green-700 font-medium' : 'text-red-600 font-medium'">
              <i :class="hasFourStages ? 'fa fa-check-circle' : 'fa fa-exclamation-triangle'" class="mr-1"></i>
              {{ hasFourStages ? '阶段划分完整：已包含引入/成长/成熟/衰退四个阶段' : '阶段划分不完整：未包含所有四个标准阶段' }}
            </span>
          </div>
          <div v-if="detail && !detail.is_complete && detail.completeness_score != null" class="text-sm text-orange-600">
            <i class="fa fa-info-circle mr-1"></i>
            不完整生命周期{{ currentConfig.entityName }} - 完整性评估得分：{{ detail.completeness_score }}/100（需要达到 80 分以上才能被评为完整周期）
          </div>
        </div>
      </div>

      <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-20">
        <div class="bg-white rounded-xl p-6 shadow-sm space-y-6">
          <h3 class="text-lg font-semibold text-gray-800">基本指标</h3>
          <div>
            <div class="flex justify-between mb-2">
              <span class="text-sm text-gray-500">总销售额</span>
              <span class="text-sm font-medium text-gray-800">{{ formatCurrency(totalRevenue) }}</span>
            </div>
            <div class="progress-bar"><div class="progress-value bg-blue-500" :style="{ width: revenuePct + '%' }"></div></div>
          </div>
          <div>
            <div class="flex justify-between mb-2">
              <span class="text-sm text-gray-500">总销量</span>
              <span class="text-sm font-medium text-gray-800">{{ totalQty }}</span>
            </div>
            <div class="progress-bar"><div class="progress-value bg-gray-500" :style="{ width: qtyPct + '%' }"></div></div>
          </div>
          <div class="grid grid-cols-2 gap-4 pt-2">
            <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
              <p class="text-xs text-gray-500 uppercase tracking-wide">峰值销售额</p>
              <p class="text-lg font-medium text-gray-800">{{ formatCurrency(detail && detail.peak_revenue) }}</p>
              <p class="text-xs text-gray-400 mt-1">{{ formatDate(detail && detail.peak_revenue_date) }}</p>
            </div>
            <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
              <p class="text-xs text-gray-500 uppercase tracking-wide">峰值销量</p>
              <p class="text-lg font-medium text-gray-800">{{ detail && detail.peak_quantity != null ? detail.peak_quantity : '-' }}</p>
              <p class="text-xs text-gray-400 mt-1">{{ formatDate(detail && detail.peak_quantity_date) }}</p>
            </div>
          </div>
          <div class="p-4 border border-gray-200 rounded-lg bg-gray-50">
            <p class="text-xs text-gray-500 uppercase tracking-wide">当前阶段说明</p>
            <p class="text-sm text-gray-700">
              处于 <span class="font-medium">{{ currentStage }}</span>，已持续
              <span class="font-medium">{{ currentStageDurationDays }}</span> 天。
            </p>
            <p class="text-xs text-gray-600 mt-1">{{ detail && detail.details ? detail.details : '-' }}</p>
            <p v-if="detail && !detail.is_complete && detail.next_stage_prediction" class="text-xs text-blue-600 mt-1">
              下一阶段预测：{{ detail.next_stage_prediction }}
            </p>
          </div>
        </div>

        <div class="lg:col-span-2 bg-white rounded-xl p-6 shadow-sm">
          <h3 class="text-lg font-semibold text-gray-800 mb-6">阶段分析</h3>
          <div class="overflow-x-auto">
            <table class="min-w-full divide-y divide-gray-200">
              <thead>
                <tr>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">阶段</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">总销售额</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">销售额占比</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">总销量</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">销量占比</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">日均销售额</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">日均销量</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">持续天数</th>
                  <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">起止日期</th>
                </tr>
              </thead>
              <tbody class="bg-white divide-y divide-gray-200">
                <tr v-for="(stats, stage) in displayStageStats" :key="stage">
                  <td class="px-4 py-3 text-sm"><span class="lifecycle-stage" :class="stageClass(stage)">{{ stage }}</span></td>
                  <td class="px-4 py-3 text-sm font-medium text-gray-800">{{ formatCurrency(stats.totalRevenue) }}</td>
                  <td class="px-4 py-3 text-sm text-gray-700">{{ formatPercent(stats.revenuePercentage) }}</td>
                  <td class="px-4 py-3 text-sm font-medium text-gray-800">{{ stats.totalQuantity != null ? stats.totalQuantity : '-' }}</td>
                  <td class="px-4 py-3 text-sm text-gray-700">{{ formatPercent(stats.quantityPercentage) }}</td>
                  <td class="px-4 py-3 text-sm text-gray-700">{{ formatCurrency(stats.avgDailyRevenue) }}</td>
                  <td class="px-4 py-3 text-sm text-gray-700">{{ stats.avgDailyQuantity != null ? stats.avgDailyQuantity : '-' }}</td>
                  <td class="px-4 py-3 text-sm text-gray-700">{{ stats.durationDays }}</td>
                  <td class="px-4 py-3 text-xs text-gray-600">{{ formatDate(stats.startDate) }} ~ {{ formatDate(stats.endDate) }}</td>
                </tr>
              </tbody>
            </table>
          </div>

          <div class="mt-8">
            <div class="flex justify-between items-center mb-4">
              <h4 class="text-base font-semibold text-gray-800">阶段对比条形图</h4>
              <div class="text-xs text-gray-500">销售额/销量/持续天数</div>
            </div>
            <div class="h-64">
              <canvas ref="stageCompareRef"></canvas>
            </div>
          </div>
        </div>
      </div>

      <div class="bg-white rounded-xl p-6 mb-20 shadow-sm">
        <div class="flex justify-between items-center mb-6">
          <h3 class="text-lg font-semibold text-gray-800">完整性评估细项</h3>
          <div class="text-sm" :class="detail && detail.is_complete ? 'text-green-600' : 'text-orange-600'">
            总分 {{ detail && detail.completeness_score != null ? detail.completeness_score : 0 }}/100
            <span class="ml-2">{{ detail && detail.is_complete ? '(已达完整标准 80 分)' : '(未达完整标准)' }}</span>
          </div>
        </div>
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          <div
            v-for="item in breakdownItems"
            :key="item.key"
            class="flex items-center justify-between border border-gray-200 rounded-lg px-4 py-3 bg-gray-50"
          >
            <div class="flex items-center gap-3">
              <span :class="item.hit ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'" class="px-3 py-1 rounded-full text-xs font-medium">
                {{ item.hit ? '命中' : '未命中' }}
              </span>
              <span class="text-sm text-gray-800">{{ item.label }}</span>
            </div>
            <span class="text-xs text-gray-500 font-medium">权重 {{ item.weight }}</span>
          </div>
        </div>
      </div>
    </template>

    <div v-else class="empty-state shadow-sm">
      <i class="el-icon-upload2"></i>
      <p>暂无{{ currentConfig.entityName }}生命周期分析结果，请先上传文件并开始分析。</p>
    </div>
  </div>
</template>

<script>
import { analyzeFile, analyzeSpuFile, getResults, getSpuResults } from '@/api/lifecycle'
import { Chart } from 'chart.js'
import { formatCurrency, formatDate } from '../../../utils/format'

const STORAGE_KEY = 'lifecycle_analysis_active_view'
const CACHE_CONFIG = {
  sku: {
    resultsKey: 'analysis_results',
    selectedKey: 'analysis_selectedSku'
  },
  spu: {
    resultsKey: 'spu_analysis_results',
    selectedKey: 'spu_analysis_selectedSpu'
  }
}
const stageOrder = ['引入期', '成长期', '成熟期', '衰退期']
const breakdownMapping = [
  { key: 'sufficient_time', label: '时间长度≥120天' },
  { key: 'reasonable_peak_position', label: '峰值位置25%-75%' },
  { key: 'significant_growth', label: '显著增长（额≥100% 或 量≥80%）' },
  { key: 'noticeable_decline', label: '明显衰退（额≤-35% 或 量≤-30%）' },
  { key: 'has_lifecycle_shape', label: '生命周期形状（额CV≥0.3 且 量CV≥0.25）' },
  { key: 'peak_significance', label: '峰值显著性（≥均值1.8x）' },
  { key: 'data_quality_check', label: '数据质量（长度≥120，峰值>0，增/退均出现）' },
  { key: 'cycle_completeness', label: '周期完整性（时间/峰值/增长/衰退/形状）' },
  { key: 'trend_consistency', label: '趋势一致性（增差≤0.8 或 退差≤0.4 或同向）' }
]

const ANALYSIS_CONFIG = {
  sku: {
    label: 'SKU分析',
    entityName: 'SKU',
    entityDisplayLabel: 'SKU编码',
    maxSizeMB: 300,
    exportPrefix: 'sku_lifecycle_results',
    fetchResults: getResults,
    analyze: analyzeFile
  },
  spu: {
    label: 'SPU分析',
    entityName: 'SPU',
    entityDisplayLabel: 'SPU名称',
    maxSizeMB: 500,
    exportPrefix: 'spu_lifecycle_results',
    fetchResults: getSpuResults,
    analyze: analyzeSpuFile
  }
}

function loadCachedAnalysis(view) {
  const config = CACHE_CONFIG[view]
  try {
    const rawResults = localStorage.getItem(config.resultsKey)
    return {
      results: rawResults ? JSON.parse(rawResults) : {},
      selected: localStorage.getItem(config.selectedKey) || ''
    }
  } catch (e) {
    return { results: {}, selected: '' }
  }
}

function createUploadState() {
  return {
    isUploading: false,
    fileName: '',
    pendingFileName: '',
    ignoreFileChange: false
  }
}

export default {
  name: 'LifecycleAnalysis',
  data() {
    return {
      activeView: localStorage.getItem(STORAGE_KEY) === 'spu' ? 'spu' : 'sku',
      trendChart: null,
      stageCompareChart: null,
      cache: {
        sku: loadCachedAnalysis('sku'),
        spu: loadCachedAnalysis('spu')
      },
      uploads: {
        sku: createUploadState(),
        spu: createUploadState()
      }
    }
  },
  computed: {
    analysisTabs() {
      return [
        { label: 'SKU分析', value: 'sku' },
        { label: 'SPU分析', value: 'spu' }
      ]
    },
    currentConfig() {
      return ANALYSIS_CONFIG[this.activeView]
    },
    upload() {
      return this.uploads[this.activeView]
    },
    currentCache() {
      return this.cache[this.activeView] || { results: {}, selected: '' }
    },
    results() {
      return this.currentCache.results || {}
    },
    hasResults() {
      return Object.keys(this.results || {}).length > 0
    },
    selectedValue: {
      get() {
        return this.currentCache.selected || ''
      },
      set(value) {
        this.setSelectedValue(value)
      }
    },
    entityOptions() {
      return Object.keys(this.results || {}).filter(key => key !== '_analysis_summary_')
    },
    detail() {
      return (this.results && this.selectedValue && this.results[this.selectedValue]) || null
    },
    currentStage() {
      return (this.detail && this.detail.current_stage) || '-'
    },
    currentStageDurationDays() {
      const stats = (this.detail && this.detail.stage_statistics) || {}
      const currentStage = this.detail && this.detail.current_stage
      if (currentStage && stats[currentStage] && stats[currentStage].durationDays != null) {
        return stats[currentStage].durationDays
      }
      return '-'
    },
    stageStats() {
      return (this.detail && this.detail.stage_statistics) || {}
    },
    displayStageStats() {
      const detail = this.detail || {}
      const stats = this.stageStats || {}
      const revenue = detail.revenue_series || detail.smoothed_revenue || []
      const qty = detail.quantity_series || detail.smoothed_quantity || []
      const rawDates = detail.date_series || []
      const labels = rawDates.map(date => formatDate(date))

      if (detail.is_complete) {
        const boundaries = (detail.stage_boundaries || []).map(item => formatDate(item.date))
        const indices = boundaries
          .map(date => labels.indexOf(date))
          .filter(index => index >= 0)
          .sort((a, b) => a - b)

        if (indices.length >= 3) {
          const ranges = [
            { name: stageOrder[0], start: 0, end: indices[0] },
            { name: stageOrder[1], start: indices[0], end: indices[1] },
            { name: stageOrder[2], start: indices[1], end: indices[2] },
            { name: stageOrder[3], start: indices[2], end: labels.length - 1 }
          ]
          const totalRevenue = revenue.reduce((sum, value) => sum + (Number(value) || 0), 0)
          const totalQty = qty.reduce((sum, value) => sum + (Number(value) || 0), 0)
          const built = {}

          ranges.forEach(range => {
            const revenueSum = revenue.slice(range.start, range.end + 1).reduce((sum, value) => sum + (Number(value) || 0), 0)
            const qtySum = qty.slice(range.start, range.end + 1).reduce((sum, value) => sum + (Number(value) || 0), 0)
            const days = Math.max(0, range.end - range.start + 1)

            built[range.name] = {
              totalRevenue: revenueSum,
              revenuePercentage: totalRevenue ? revenueSum / totalRevenue * 100 : 0,
              totalQuantity: qtySum,
              quantityPercentage: totalQty ? qtySum / totalQty * 100 : 0,
              avgDailyRevenue: days ? revenueSum / days : 0,
              avgDailyQuantity: days ? qtySum / days : 0,
              durationDays: days,
              startDate: rawDates[range.start],
              endDate: rawDates[range.end]
            }
          })

          return built
        }

        if (stageOrder.every(stage => stats[stage])) {
          return stats
        }
      }

      const actual = {}
      const keys = stageOrder.filter(stage => stats[stage]).concat(Object.keys(stats).filter(stage => stageOrder.indexOf(stage) === -1))

      keys.forEach(key => {
        const value = stats[key] || {}
        let duration = value.durationDays
        const startIndex = labels.indexOf(formatDate(value.startDate))
        const endIndex = labels.indexOf(formatDate(value.endDate))
        if (startIndex >= 0 && endIndex >= 0 && endIndex >= startIndex) {
          duration = Math.max(0, endIndex - startIndex + 1)
        }
        actual[key] = Object.assign({}, value, { durationDays: duration })
      })

      return actual
    },
    breakdownItems() {
      const detail = this.detail || {}
      const completionDetails = detail.completion_details || {}
      return breakdownMapping.map(item => {
        const current = completionDetails[item.key] || {}
        return {
          key: item.key,
          label: item.label,
          hit: !!current.hit,
          weight: current.weight != null ? current.weight : '-'
        }
      })
    },
    hasFourStages() {
      return stageOrder.every(stage => !!this.stageStats[stage])
    },
    totalRevenue() {
      if (this.detail && this.detail.total_revenue != null) return this.detail.total_revenue
      return Object.values(this.stageStats || {}).reduce((sum, value) => sum + (value.totalRevenue || 0), 0)
    },
    totalQty() {
      if (this.detail && this.detail.total_quantity != null) return this.detail.total_quantity
      return Object.values(this.stageStats || {}).reduce((sum, value) => sum + (value.totalQuantity || 0), 0)
    },
    revenuePct() {
      return 100
    },
    qtyPct() {
      return 100
    }
  },
  mounted() {
    this.initCurrentView()
  },
  beforeDestroy() {
    this.destroyCharts()
  },
  watch: {
    activeView() {
      localStorage.setItem(STORAGE_KEY, this.activeView)
      this.resetUploaderSelection()
      this.initCurrentView()
    },
    detail() {
      this.$nextTick(() => {
        this.renderTrend()
        this.renderStageCompare()
      })
    },
    results() {
      this.ensureCurrentSelection()
    }
  },
  methods: {
    formatCurrency,
    formatDate,
    switchAnalysis(view) {
      if (view === this.activeView) return
      this.activeView = view
    },
    initCurrentView() {
      if (!this.hasResults) {
        this.getList()
        return
      }

      this.ensureCurrentSelection()
      this.$nextTick(() => {
        this.renderTrend()
        this.renderStageCompare()
      })
    },
    getCurrentUploadState() {
      return this.uploads[this.activeView]
    },
    persistCurrentCache() {
      const config = CACHE_CONFIG[this.activeView]
      const cache = this.currentCache
      try {
        localStorage.setItem(config.resultsKey, JSON.stringify(cache.results || {}))
        localStorage.setItem(config.selectedKey, cache.selected || '')
      } catch (e) {}
    },
    setCurrentResults(results) {
      this.$set(this.cache, this.activeView, Object.assign({}, this.currentCache, {
        results: results || {}
      }))
      this.persistCurrentCache()
    },
    setSelectedValue(value) {
      this.$set(this.cache, this.activeView, Object.assign({}, this.currentCache, {
        selected: value || ''
      }))
      this.persistCurrentCache()
    },
    extractResultsPayload(response) {
      if (!response) return null
      if (response.success && response.data) return response.data
      if (response.code === 200 && response.data) return response.data
      if (response.data && typeof response.data === 'object') return response.data
      if (!response.message && !response.msg && typeof response === 'object') return response
      return null
    },
    ensureCurrentSelection() {
      if (!this.hasResults) {
        this.selectedValue = ''
        this.destroyCharts()
        return
      }

      if (!this.selectedValue || !this.results[this.selectedValue]) {
        const first = this.pickFirstValue(this.results)
        if (first) {
          this.selectedValue = first
        }
      }
    },
    getList() {
      this.currentConfig.fetchResults().then(response => {
        const results = this.extractResultsPayload(response)
        if (results) {
          this.setCurrentResults(results)
          const first = this.pickFirstValue(results)
          this.setSelectedValue(first)
          this.$nextTick(() => {
            this.renderTrend()
            this.renderStageCompare()
          })
        } else {
          this.setCurrentResults({})
        }
      }).catch(() => {
        this.setCurrentResults({})
        this.destroyCharts()
      })
    },
    handleFileChange(file, fileList) {
      const upload = this.getCurrentUploadState()
      if (upload.ignoreFileChange) return
      if (!fileList || fileList.length === 0 || !file || !file.raw) return

      upload.pendingFileName = file.name
      upload.fileName = ''
    },
    beforeUpload(file) {
      const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
        file.type === 'application/vnd.ms-excel' ||
        file.type === 'text/csv' ||
        file.name.endsWith('.xlsx') ||
        file.name.endsWith('.xls') ||
        file.name.endsWith('.csv')
      const isLtLimit = file.size / 1024 / 1024 < this.currentConfig.maxSizeMB

      if (!isExcel) {
        this.$modal.msgError('上传文件只能是 xlsx/xls/csv 格式')
        return false
      }
      if (!isLtLimit) {
        this.$modal.msgError(`上传文件大小不能超过 ${this.currentConfig.maxSizeMB}MB`)
        return false
      }
      return true
    },
    customUpload(options) {
      const file = options.file
      const upload = this.getCurrentUploadState()
      upload.isUploading = true

      this.currentConfig.analyze(file).then(response => {
        upload.isUploading = false
        if (response && response.success) {
          const results = response.data || {}
          this.setCurrentResults(results)
          const first = this.pickFirstValue(results)
          this.setSelectedValue(first)
          upload.fileName = upload.pendingFileName || file.name
          upload.pendingFileName = ''
          this.$modal.msgSuccess(`${this.currentConfig.entityName}生命周期分析完成`)
          this.$nextTick(() => {
            this.renderTrend()
            this.renderStageCompare()
          })
          options.onSuccess(response)
        } else {
          const message = response && (response.msg || response.message) ? response.msg || response.message : '分析失败'
          this.$modal.msgError(message)
          options.onError(new Error(message))
        }
      }).catch(error => {
        upload.isUploading = false
        const message = (error && error.message) || '文件上传失败，请重试'
        this.$modal.msgError(message)
        options.onError(error)
      }).finally(() => {
        this.resetUploaderSelection()
      })
    },
    submitUpload() {
      const target = this.$refs.toolbarUpload
      const fileList = target && target.uploadFiles ? target.uploadFiles : []
      if (!fileList || fileList.length === 0) {
        this.$modal.msgError('请选择要上传的文件')
        return
      }
      target.submit()
    },
    resetUploaderSelection() {
      if (!this.$refs.toolbarUpload) return
      const upload = this.getCurrentUploadState()
      upload.ignoreFileChange = true
      this.$refs.toolbarUpload.clearFiles()
      this.$nextTick(() => {
        upload.ignoreFileChange = false
      })
    },
    formatPercent(value) {
      if (value == null || Number.isNaN(value)) return '-'
      return `${Number(value).toFixed(1)}%`
    },
    stageClass(stage) {
      if (!stage) return ''
      if (stage.indexOf('引入') >= 0 || stage.indexOf('导入') >= 0) return 'intro'
      if (stage.indexOf('成长') >= 0) return 'growth'
      if (stage.indexOf('成熟') >= 0) return 'maturity'
      if (stage.indexOf('衰退') >= 0) return 'decline'
      return ''
    },
    normalizeDate(dateStr) {
      if (!dateStr) return ''
      const match = String(dateStr).match(/(\d{4}-\d{2}-\d{2})/)
      if (match) return match[1]
      const parts = String(dateStr).split('T')
      return parts[0].split(' ')[0]
    },
    destroyCharts() {
      if (this.trendChart) {
        this.trendChart.destroy()
        this.trendChart = null
      }
      if (this.stageCompareChart) {
        this.stageCompareChart.destroy()
        this.stageCompareChart = null
      }
    },
    renderTrend() {
      const canvas = this.$refs.lifeCycleTrendRef
      if (!canvas || !this.detail) {
        if (this.trendChart) {
          this.trendChart.destroy()
          this.trendChart = null
        }
        return
      }

      const detail = this.detail || {}
      const revenue = detail.smoothed_revenue || detail.revenue_series || []
      const qty = detail.smoothed_quantity || detail.quantity_series || []
      const rawDates = detail.date_series || []
      const labels = rawDates.map(date => formatDate(date))
      const boundaries = (detail.stage_boundaries || []).map(boundary => ({
        type: boundary.type,
        date: boundary.date ? formatDate(boundary.date) : null,
        index: boundary.index
      }))

      const stageColors = {
        [stageOrder[0]]: 'rgba(59,130,246,0.08)',
        [stageOrder[1]]: 'rgba(16,185,129,0.10)',
        [stageOrder[2]]: 'rgba(245,158,11,0.10)',
        [stageOrder[3]]: 'rgba(239,68,68,0.08)'
      }

      const segments = []
      const stagesMap = detail.stages_map || []

      if (stagesMap.length > 0 && stagesMap.length === labels.length) {
        let currentStage = stagesMap[0]
        let segmentStart = 0

        for (let i = 1; i < stagesMap.length; i++) {
          if (stagesMap[i] !== currentStage) {
            if (currentStage && stageColors[currentStage]) {
              segments.push({
                start: segmentStart,
                end: i - 1,
                stage: currentStage
              })
            }
            currentStage = stagesMap[i]
            segmentStart = i
          }
        }

        if (currentStage && stageColors[currentStage] && segmentStart < stagesMap.length) {
          segments.push({
            start: segmentStart,
            end: stagesMap.length - 1,
            stage: currentStage
          })
        }
      } else {
        const stats = this.displayStageStats || {}
        const orderedStages = stageOrder.filter(stage => stats[stage]).concat(Object.keys(stats).filter(stage => stageOrder.indexOf(stage) === -1))
        orderedStages.forEach(stage => {
          const startIndex = labels.indexOf(formatDate(stats[stage] && stats[stage].startDate))
          const endIndex = labels.indexOf(formatDate(stats[stage] && stats[stage].endDate))
          if (startIndex >= 0 && endIndex >= 0 && endIndex >= startIndex) {
            segments.push({ start: startIndex, end: endIndex, stage })
          }
        })
      }

      const peakRevenueDate = detail.peak_revenue_date
      const peakQtyDate = detail.peak_quantity_date
      let peakRevenueIndex = -1
      let peakQtyIndex = -1

      if (peakRevenueDate && rawDates.length > 0) {
        const targetDate = this.normalizeDate(peakRevenueDate)
        peakRevenueIndex = rawDates.findIndex(date => this.normalizeDate(date) === targetDate)
        if (peakRevenueIndex < 0 && detail.revenue_peak_idx != null && detail.revenue_peak_idx < rawDates.length) {
          peakRevenueIndex = detail.revenue_peak_idx
        }
      }

      if (peakQtyDate && rawDates.length > 0) {
        const targetDate = this.normalizeDate(peakQtyDate)
        peakQtyIndex = rawDates.findIndex(date => this.normalizeDate(date) === targetDate)
        if (peakQtyIndex < 0 && detail.quantity_peak_idx != null && detail.quantity_peak_idx < rawDates.length) {
          peakQtyIndex = detail.quantity_peak_idx
        }
      }

      const boundaryIndices = []
      boundaries.forEach(boundary => {
        let index = -1
        if (boundary.index != null && boundary.index >= 0 && boundary.index < rawDates.length) {
          index = boundary.index
        } else if (boundary.date) {
          const targetDate = this.normalizeDate(boundary.date)
          index = rawDates.findIndex(date => this.normalizeDate(date) === targetDate)
        }

        if (index >= 0 && index < rawDates.length) {
          boundaryIndices.push({ index, type: boundary.type })
        }
      })

      if (this.trendChart) this.trendChart.destroy()
      this.trendChart = new Chart(canvas, {
        type: 'line',
        data: {
          labels,
          datasets: [
            {
              label: '销售额',
              data: revenue,
              borderColor: '#3b82f6',
              backgroundColor: 'rgba(59,130,246,0.15)',
              lineTension: 0.25,
              pointRadius: 0,
              pointHoverRadius: 6,
              pointHoverBackgroundColor: '#3b82f6',
              pointHoverBorderColor: '#ffffff',
              pointHoverBorderWidth: 2
            },
            {
              label: '销量',
              data: qty,
              borderColor: '#64748b',
              backgroundColor: 'rgba(100,116,139,0.15)',
              lineTension: 0.25,
              pointRadius: 0,
              pointHoverRadius: 6,
              pointHoverBackgroundColor: '#64748b',
              pointHoverBorderColor: '#ffffff',
              pointHoverBorderWidth: 2
            }
          ]
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          tooltips: {
            enabled: true,
            backgroundColor: 'rgba(0,0,0,0.8)',
            titleFontColor: '#ffffff',
            bodyFontColor: '#ffffff',
            borderColor: '#374151',
            borderWidth: 1,
            cornerRadius: 6,
            displayColors: true,
            callbacks: {
              title(context) {
                return `日期: ${context[0].label}`
              },
              label(context, data) {
                const datasetLabel = data.datasets[context.datasetIndex].label
                return `${datasetLabel}: ${Number(context.value).toLocaleString()}`
              }
            }
          },
          hover: {
            mode: 'index',
            intersect: false
          },
          scales: {
            xAxes: [{
              ticks: {
                maxRotation: 0,
                autoSkip: true,
                maxTicksLimit: 12,
                callback(value, index) {
                  return labels[index]
                }
              }
            }]
          }
        },
        plugins: [{
          id: 'stage-backgrounds',
          beforeDatasetsDraw(chart) {
            const ctx = chart.ctx
            const chartArea = chart.chartArea
            const xScale = chart.scales['x-axis-0']

            if (!segments.length) return

            ctx.save()
            segments.forEach(segment => {
              const startX = xScale.getPixelForValue(segment.start)
              const endX = xScale.getPixelForValue(segment.end)
              const color = stageColors[segment.stage] || 'rgba(0,0,0,0.04)'

              ctx.fillStyle = color
              ctx.fillRect(startX, chartArea.top, endX - startX, chartArea.bottom - chartArea.top)

              ctx.fillStyle = '#374151'
              ctx.font = 'bold 12px sans-serif'
              ctx.textAlign = 'left'
              ctx.fillText(segment.stage, startX + 4, chartArea.top + 14)
            })
            ctx.restore()
          }
        }, {
          id: 'stage-markers',
          afterDatasetsDraw(chart) {
            const ctx = chart.ctx
            const chartArea = chart.chartArea
            const xScale = chart.scales['x-axis-0']
            const yScale = chart.scales['y-axis-0']
            ctx.save()

            boundaryIndices.forEach(boundary => {
              const x = xScale.getPixelForValue(boundary.index)

              ctx.strokeStyle = '#ef4444'
              ctx.setLineDash([8, 4])
              ctx.lineWidth = 2
              ctx.beginPath()
              ctx.moveTo(x, chartArea.top)
              ctx.lineTo(x, chartArea.bottom)
              ctx.stroke()
              ctx.setLineDash([])

              const labelText = boundary.type
              const labelWidth = ctx.measureText(labelText).width + 8
              const labelHeight = 16
              const labelX = x - labelWidth / 2
              const labelY = chartArea.top + 8

              ctx.fillStyle = 'rgba(239, 68, 68, 0.1)'
              ctx.fillRect(labelX, labelY, labelWidth, labelHeight)

              ctx.strokeStyle = '#ef4444'
              ctx.lineWidth = 1
              ctx.strokeRect(labelX, labelY, labelWidth, labelHeight)

              ctx.fillStyle = '#ef4444'
              ctx.font = 'bold 11px sans-serif'
              ctx.textAlign = 'center'
              ctx.fillText(labelText, x, labelY + 12)
            })

            if (peakRevenueIndex >= 0 && peakRevenueIndex < revenue.length && revenue[peakRevenueIndex] != null) {
              const x = xScale.getPixelForValue(peakRevenueIndex)
              const y = yScale.getPixelForValue(revenue[peakRevenueIndex])

              ctx.fillStyle = '#dc2626'
              ctx.beginPath()
              ctx.arc(x, y, 5, 0, Math.PI * 2)
              ctx.fill()

              ctx.strokeStyle = '#ffffff'
              ctx.lineWidth = 2
              ctx.stroke()

              ctx.fillStyle = '#dc2626'
              ctx.font = 'bold 12px sans-serif'
              ctx.textAlign = 'left'
              ctx.fillText('销售额峰值', x + 10, y - 10)

              ctx.font = '11px sans-serif'
              ctx.fillText(`￥${Number(revenue[peakRevenueIndex]).toLocaleString()}`, x + 10, y + 2)
            }

            if (peakQtyIndex >= 0 && peakQtyIndex < qty.length && qty[peakQtyIndex] != null) {
              const x = xScale.getPixelForValue(peakQtyIndex)
              const y = yScale.getPixelForValue(qty[peakQtyIndex])

              ctx.fillStyle = '#2563eb'
              ctx.beginPath()
              ctx.arc(x, y, 5, 0, Math.PI * 2)
              ctx.fill()

              ctx.strokeStyle = '#ffffff'
              ctx.lineWidth = 2
              ctx.stroke()

              ctx.fillStyle = '#2563eb'
              ctx.font = 'bold 12px sans-serif'
              ctx.textAlign = 'left'
              ctx.fillText('销量峰值', x + 10, y - 10)

              ctx.font = '11px sans-serif'
              ctx.fillText(`${String(qty[peakQtyIndex]).toLocaleString()}件`, x + 10, y + 2)
            }
            ctx.restore()
          }
        }]
      })
    },
    renderStageCompare() {
      const canvas = this.$refs.stageCompareRef
      if (!canvas || !this.detail) {
        if (this.stageCompareChart) {
          this.stageCompareChart.destroy()
          this.stageCompareChart = null
        }
        return
      }

      const stats = this.displayStageStats || {}
      const labels = stageOrder.filter(stage => stats[stage]).concat(Object.keys(stats).filter(stage => stageOrder.indexOf(stage) === -1))
      const revenueData = labels.map(stage => (stats[stage] && stats[stage].totalRevenue) || 0)
      const qtyData = labels.map(stage => (stats[stage] && stats[stage].totalQuantity) || 0)
      const durationData = labels.map(stage => (stats[stage] && stats[stage].durationDays) || 0)

      if (this.stageCompareChart) this.stageCompareChart.destroy()
      this.stageCompareChart = new Chart(canvas, {
        type: 'bar',
        data: {
          labels,
          datasets: [
            { label: '销售额', data: revenueData, backgroundColor: 'rgba(59,130,246,0.7)' },
            { label: '销量', data: qtyData, backgroundColor: 'rgba(16,185,129,0.7)' },
            { label: '持续天数', data: durationData, backgroundColor: 'rgba(245,158,11,0.7)' }
          ]
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          scales: {
            xAxes: [{ stacked: false }],
            yAxes: [{ ticks: { beginAtZero: true } }]
          }
        }
      })
    },
    formatUploadDate(date) {
      const target = date instanceof Date ? date : new Date(date)
      if (Number.isNaN(target.getTime())) return ''
      const year = target.getFullYear()
      const month = String(target.getMonth() + 1).padStart(2, '0')
      const day = String(target.getDate()).padStart(2, '0')
      return `${year}-${month}-${day}`
    },
    exportResults() {
      if (!this.hasResults) {
        this.$modal.msgError('暂无可导出的分析结果')
        return
      }
      const payload = JSON.stringify(this.results || {}, null, 2)
      const blob = new Blob([payload], { type: 'application/json;charset=utf-8' })
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `${this.currentConfig.exportPrefix}_${this.formatUploadDate(new Date())}.json`
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      URL.revokeObjectURL(url)
    },
    pickFirstValue(results) {
      const keys = Object.keys(results || {})
      return keys.find(key => key !== '_analysis_summary_') || ''
    }
  }
}
</script>

<style scoped lang="scss">
.app-container {
  padding: 20px;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;

  h2 {
    font-size: 24px;
    font-weight: 600;
    color: #303133;
    margin-bottom: 8px;

    i {
      margin-right: 8px;
      color: #409eff;
    }
  }

  .page-desc {
    color: #909399;
    font-size: 14px;
    margin: 0;
  }
}

.analysis-switch {
  display: inline-flex;
  padding: 4px;
  border-radius: 10px;
  background: #f3f6fb;
  border: 1px solid #dce6f2;
}

.switch-btn {
  border: none;
  background: transparent;
  color: #606266;
  padding: 8px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.switch-btn.active {
  background: #ffffff;
  color: #2563eb;
  box-shadow: 0 2px 8px rgba(37, 99, 235, 0.12);
}

.mb-20 { margin-bottom: 20px; }

.upload-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #ffffff;
  border: 1px solid #e6eaf2;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 16px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.toolbar-upload ::v-deep .el-upload {
  display: inline-flex;
}

.toolbar-status {
  font-size: 13px;
  color: #16a34a;
  background: #f0fdf4;
  border: 1px solid #dcfce7;
  border-radius: 6px;
  padding: 6px 10px;
}

.toolbar-status.muted {
  color: #6b7280;
  background: #f8fafc;
  border-color: #e2e8f0;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 280px;
  background: #ffffff;
  border-radius: 8px;
  border: 1px solid #e6eaf2;
  color: #909399;
  font-size: 14px;

  i {
    font-size: 40px;
    color: #c0c4cc;
  }

  p {
    margin: 0;
  }
}

.p-6 { padding: 24px; }
.p-4 { padding: 16px; }
.px-3 { padding-left: 12px; padding-right: 12px; }
.px-4 { padding-left: 16px; padding-right: 16px; }
.py-1 { padding-top: 4px; padding-bottom: 4px; }
.py-2 { padding-top: 8px; padding-bottom: 8px; }
.py-3 { padding-top: 12px; padding-bottom: 12px; }
.pt-2 { padding-top: 8px; }
.mb-2 { margin-bottom: 8px; }
.mb-4 { margin-bottom: 16px; }
.mb-6 { margin-bottom: 24px; }
.mb-8 { margin-bottom: 32px; }
.mt-1 { margin-top: 4px; }
.mt-4 { margin-top: 16px; }
.mt-8 { margin-top: 32px; }
.ml-2 { margin-left: 8px; }
.mr-1 { margin-right: 4px; }

.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: 8px; }
.gap-3 { gap: 12px; }
.gap-4 { gap: 16px; }
.gap-8 { gap: 20px; }

.grid { display: grid; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.lg\:col-span-2 { grid-column: span 2 / span 2; }
.overflow-x-auto { overflow-x: auto; }
.min-w-full { min-width: 100%; }

.text-base { font-size: 16px; line-height: 1.5; }
.text-lg { font-size: 18px; line-height: 1.5; }
.text-sm { font-size: 14px; line-height: 1.5; }
.text-xs { font-size: 12px; line-height: 1.4; }
.font-semibold { font-weight: 600; }
.font-medium { font-weight: 500; }
.text-left { text-align: left; }
.uppercase { text-transform: uppercase; }
.tracking-wide { letter-spacing: 0.04em; }
.tracking-wider { letter-spacing: 0.06em; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.text-gray-800 { color: #303133; }
.text-gray-700 { color: #606266; }
.text-gray-600 { color: #909399; }
.text-gray-500 { color: #909399; }
.text-gray-400 { color: #9ca3af; }
.text-blue-600 { color: #2563eb; }
.text-green-700 { color: #15803d; }
.text-green-600 { color: #16a34a; }
.text-red-600 { color: #dc2626; }
.text-orange-600 { color: #ea580c; }
.text-yellow-800 { color: #92400e; }

.bg-white { background: #ffffff; }
.bg-gray-50 { background: #f9fafb; }
.bg-gray-500 { background: #6b7280; }
.bg-blue-500 { background: #3b82f6; }
.bg-green-100 { background: #dcfce7; }
.bg-red-100 { background: #fee2e2; }

.bg-gradient-to-r { background-image: linear-gradient(to right, var(--from-color), var(--to-color)); }
.from-yellow-100 { --from-color: #fef9c3; }
.to-amber-100 { --to-color: #fef3c7; }

.border { border-width: 1px; border-style: solid; }
.border-gray-200 { border-color: #e5e7eb; }
.border-gray-300 { border-color: #d1d5db; }
.rounded-xl { border-radius: 8px; }
.rounded-lg { border-radius: 10px; }
.rounded-full { border-radius: 9999px; }
.shadow-sm { border: 1px solid #e6eaf2; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); }

.h-96 { height: 400px; }
.h-64 { height: 256px; }
.w-3 { width: 12px; }
.h-3 { height: 12px; }

.space-y-2 > * + * { margin-top: 8px; }
.space-y-6 > * + * { margin-top: 24px; }
.divide-y > * + * { border-top: 1px solid #e5e7eb; }
.divide-gray-200 > * + * { border-top-color: #e5e7eb; }

.focus\:outline-none:focus { outline: none; }
.focus\:border-transparent:focus { border-color: transparent; }
.focus\:ring-2:focus { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.35); }
.focus\:ring-blue-500:focus { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.45); }

.entity-select {
  min-width: 220px;
  background: #ffffff;
}

table {
  border-collapse: separate;
  border-spacing: 0;
}

thead tr {
  background: #f8fafc;
}

thead th:first-child {
  border-top-left-radius: 8px;
}

thead th:last-child {
  border-top-right-radius: 8px;
}

@media (max-width: 767px) {
  .page-header,
  .upload-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .analysis-switch,
  .toolbar-left {
    width: 100%;
  }

  .switch-btn {
    flex: 1;
  }
}

@media (min-width: 768px) {
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.progress-bar { height: 8px; background: #f3f4f6; border-radius: 999px; overflow: hidden; }
.progress-value { height: 100%; border-radius: 999px; }

.lifecycle-stage { padding: 2px 8px; border-radius: 999px; font-size: 12px; }
.lifecycle-stage.intro { background: #dbeafe; color: #1e40af; }
.lifecycle-stage.growth { background: #dcfce7; color: #166534; }
.lifecycle-stage.maturity { background: #fef3c7; color: #92400e; }
.lifecycle-stage.decline { background: #fee2e2; color: #991b1b; }
</style>
