<template>
  <div class="sales-page">
    <section class="header-bar">
      <h1 class="header-title">趋势预测</h1>
      <div class="header-actions">
        <el-upload
          ref="salesUpload"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          accept=".xlsx,.xls,.csv"
          :on-change="handleSalesFileChange"
        >
          <el-button size="small" plain icon="el-icon-upload2">销售数据</el-button>
        </el-upload>
        <el-upload
          ref="productUpload"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          accept=".xlsx,.xls"
          :on-change="handleProductFileChange"
        >
          <el-button size="small" plain icon="el-icon-folder-add">产品资料</el-button>
        </el-upload>
        <el-button size="small" type="primary" :loading="loading" icon="el-icon-caret-right" @click="runPrediction">开始预测</el-button>
        <el-button size="small" icon="el-icon-download" :disabled="!hasPrediction" @click="exportResults">导出</el-button>
      </div>
    </section>

    <section class="filter-bar">
      <div class="filter-group">
        <label>预测类型</label>
        <el-select v-model="predictType" size="small">
          <el-option label="按 SKU" value="sku" />
          <el-option label="按品类" value="category" />
        </el-select>
      </div>
      <div class="filter-group wide">
        <label>{{ predictType === 'sku' ? 'SKU' : '品类' }}</label>
        <el-select v-model="selectedItem" size="small" filterable placeholder="请选择">
          <el-option v-for="item in selectOptions" :key="item" :label="item" :value="item" />
        </el-select>
      </div>
      <div class="filter-group">
        <label>预测周期</label>
        <el-select v-model.number="predictionPeriod" size="small">
          <el-option label="未来 7 天" :value="7" />
          <el-option label="未来 15 天" :value="15" />
          <el-option label="未来 30 天" :value="30" />
          <el-option label="未来 60 天" :value="60" />
        </el-select>
      </div>
      <div class="file-hint">
        <span v-if="salesFileName">销售数据：{{ salesFileName }}</span>
        <span v-if="productFileName">产品资料：{{ productFileName }}</span>
        <span v-if="!salesFileName && !productFileName">请先上传历史销售数据</span>
      </div>
    </section>

    <el-progress v-if="loading" :percentage="progress" :stroke-width="8" class="progress" />

    <section class="chart-panel">
      <div class="chart-header">
        <div class="chart-summary">
          <div>
            <span class="summary-label">预测总量</span>
            <strong>{{ formatNumber(predictedTotal) }}</strong>
          </div>
          <div>
            <span class="summary-label">环比变化</span>
            <strong :class="growthRate >= 0 ? 'up' : 'down'">{{ growthRateText }}</strong>
          </div>
        </div>
        <div class="legend">
          <span><i class="solid blue"></i>历史销量</span>
          <span><i class="dashed orange"></i>预测销量</span>
          <span><i class="box gray"></i>置信区间</span>
        </div>
      </div>
      <div ref="trendChart" class="chart"></div>
    </section>

    <section class="data-grid">
      <div class="panel">
        <div class="panel-head">
          <h3>预测数值表</h3>
          <el-input v-model="dateKeyword" size="small" prefix-icon="el-icon-search" placeholder="搜索日期" />
        </div>
        <el-table :data="filteredPredictionRows" size="small" height="330">
          <el-table-column prop="date" label="日期" min-width="120" />
          <el-table-column prop="quantity" label="预测销量" min-width="120">
            <template slot-scope="{ row }">{{ formatNumber(row.quantity) }}</template>
          </el-table-column>
          <el-table-column prop="lower" label="置信区间下限" min-width="130">
            <template slot-scope="{ row }">{{ formatNumber(row.lower) }}</template>
          </el-table-column>
          <el-table-column prop="upper" label="置信区间上限" min-width="130">
            <template slot-scope="{ row }">{{ formatNumber(row.upper) }}</template>
          </el-table-column>
        </el-table>
      </div>

      <div class="panel metric-panel">
        <h3>关键指标</h3>
        <div class="metric-card">
          <span>预测期总销量</span>
          <strong>{{ formatNumber(predictedTotal) }}</strong>
        </div>
        <div class="metric-card orange">
          <span>预测期日均销量</span>
          <strong>{{ formatNumber(predictedAverage) }}</strong>
        </div>
        <div class="metric-card green">
          <span>预测置信度</span>
          <strong>{{ effectiveConfidence }}%</strong>
          <el-progress :percentage="effectiveConfidence" :show-text="false" color="#67c23a" />
        </div>
        <div class="metric-card blue">
          <span>高增长期</span>
          <strong>{{ highGrowthPeriod }}</strong>
        </div>
      </div>
    </section>
  </div>
</template>

<script>
import * as echarts from 'echarts'
import {
  analyzeSaleTrendWithFile,
  getSaleTrendResults,
  predictSalesTrend,
  uploadProductFile,
  getUploadProgress
} from '@/api/client'

export default {
  name: 'SalesTrendPrediction',
  data() {
    return {
      chart: null,
      loading: false,
      progress: 0,
      progressTimer: null,
      salesFile: null,
      productFile: null,
      salesFileName: '',
      productFileName: '',
      productUploaded: false,
      predictType: 'sku',
      selectedItem: '',
      predictionPeriod: 7,
      analysisResults: {},
      predictionResults: {},
      dateKeyword: '',
      confidence: 92
    }
  },
  computed: {
    selectOptions() {
      return this.predictType === 'sku'
        ? (this.analysisResults.sku_list || [])
        : (this.analysisResults.category_list || [])
    },
    historicalDetail() {
      if (!this.selectedItem) return {}
      return this.predictType === 'sku'
        ? ((this.analysisResults.data || {})[this.selectedItem] || {})
        : ((this.analysisResults.categories || {})[this.selectedItem] || {})
    },
    predictionDetail() {
      if (!this.selectedItem) return {}
      if (this.predictType === 'sku') {
        return ((this.predictionResults.sku_predictions || {})[this.selectedItem] || this.predictionResults.overall_prediction || {})
      }
      return ((this.predictionResults.category_predictions || {})[this.selectedItem] || this.predictionResults.overall_prediction || {})
    },
    hasPrediction() {
      return (this.predictionDetail.quantity_series || []).length > 0
    },
    predictedTotal() {
      return (this.predictionDetail.quantity_series || []).reduce((sum, value) => sum + Number(value || 0), 0)
    },
    predictedAverage() {
      const series = this.predictionDetail.quantity_series || []
      return series.length ? this.predictedTotal / series.length : 0
    },
    historicalAverage() {
      const series = this.historicalDetail.quantity_series || []
      return series.length ? series.reduce((sum, value) => sum + Number(value || 0), 0) / series.length : 0
    },
    growthRate() {
      if (!this.historicalAverage) return 0
      return ((this.predictedAverage - this.historicalAverage) / this.historicalAverage) * 100
    },
    growthRateText() {
      const sign = this.growthRate >= 0 ? '+' : ''
      return `${sign}${this.growthRate.toFixed(1)}%`
    },
    predictionRows() {
      const dates = this.predictionDetail.date_series || []
      const values = this.predictionDetail.quantity_series || []
      return dates.map((date, index) => {
        const quantity = Number(values[index] || 0)
        const band = Math.max(quantity * this.confidenceBandRate, 1)
        return {
          date,
          quantity,
          lower: Math.max(0, quantity - band),
          upper: quantity + band
        }
      })
    },
    filteredPredictionRows() {
      if (!this.dateKeyword) return this.predictionRows
      return this.predictionRows.filter(row => row.date.includes(this.dateKeyword))
    },
    highGrowthPeriod() {
      if (!this.predictionRows.length) return '-'
      const sorted = [...this.predictionRows].sort((a, b) => b.quantity - a.quantity)
      const first = sorted[0]
      return first ? first.date : '-'
    },
    effectiveConfidence() {
      const detailParams = this.predictionDetail.model_params || {}
      const globalParams = this.predictionResults.control_params || {}
      return Math.round(Number(detailParams.confidence || globalParams.confidence || this.confidence))
    },
    confidenceBandRate() {
      return Math.max(0.04, Math.min(0.25, (100 - this.effectiveConfidence) / 100 + 0.04))
    }
  },
  watch: {
    selectedItem() {
      this.$nextTick(this.renderChart)
    },
    predictType() {
      this.selectedItem = this.selectOptions[0] || ''
      if (this.selectedItem && this.analysisResults.summary) this.runPrediction(false)
    },
    predictionPeriod() {
      if (this.selectedItem && this.analysisResults.summary) this.runPrediction(false)
    }
  },
  mounted() {
    this.chart = echarts.init(this.$refs.trendChart)
    window.addEventListener('resize', this.resizeChart)
    this.loadCachedResults()
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.resizeChart)
    this.stopProgress()
    if (this.chart) this.chart.dispose()
  },
  methods: {
    handleSalesFileChange(file) {
      this.salesFile = file.raw
      this.salesFileName = file.name
    },
    handleProductFileChange(file) {
      this.productFile = file.raw
      this.productFileName = file.name
      this.productUploaded = false
    },
    async loadCachedResults() {
      try {
        const response = await getSaleTrendResults()
        if (response && response.success) {
          this.analysisResults = response.data || {}
          this.selectedItem = this.selectOptions[0] || ''
          if (this.selectedItem) await this.runPrediction(false)
        }
      } catch (e) {
        this.analysisResults = {}
      }
    },
    async runPrediction(requireFile = true) {
      if (requireFile && !this.salesFile && !this.analysisResults.summary) {
        this.$modal.msgError('请先选择销售数据文件')
        return
      }
      this.loading = true
      this.startProgress()
      try {
        if (this.productFile && !this.productUploaded) {
          await uploadProductFile(this.productFile)
          this.productUploaded = true
        }
        if (this.salesFile) {
          const analyzed = await analyzeSaleTrendWithFile(this.salesFile)
          if (!analyzed.success) throw new Error(analyzed.message || '销售数据分析失败')
          this.analysisResults = analyzed.data || {}
          this.selectedItem = this.selectOptions[0] || this.selectedItem
          this.salesFile = null
        }
        const predicted = await predictSalesTrend({ predict_days: this.predictionPeriod })
        if (!predicted.success) throw new Error(predicted.message || '预测失败')
        this.predictionResults = predicted.data || {}
        this.$nextTick(this.renderChart)
        this.$modal.msgSuccess('预测完成')
      } catch (error) {
        this.$modal.msgError(error.message || '预测失败，请检查服务状态')
      } finally {
        this.loading = false
        this.progress = 100
        this.stopProgress()
      }
    },
    startProgress() {
      this.progress = 5
      this.stopProgress()
      this.progressTimer = setInterval(async () => {
        try {
          const res = await getUploadProgress()
          if (res && res.success) this.progress = Math.min(99, res.progress || this.progress)
        } catch (e) {
          this.progress = Math.min(95, this.progress + 8)
        }
      }, 600)
    },
    stopProgress() {
      if (this.progressTimer) clearInterval(this.progressTimer)
      this.progressTimer = null
    },
    renderChart() {
      if (!this.chart) return
      const histDates = this.historicalDetail.date_series || []
      const histValues = this.historicalDetail.quantity_series || []
      const predDates = this.predictionDetail.date_series || []
      const predValues = this.predictionDetail.quantity_series || []
      const labels = [...histDates, ...predDates]
      const lower = predValues.map(v => Math.max(0, Number(v || 0) * (1 - this.confidenceBandRate)))
      const upper = predValues.map(v => Number(v || 0) * (1 + this.confidenceBandRate))
      this.chart.setOption({
        tooltip: { trigger: 'axis' },
        grid: { top: 30, left: 45, right: 24, bottom: 45 },
        xAxis: { type: 'category', data: labels, boundaryGap: false },
        yAxis: { type: 'value' },
        series: [
          { name: '历史销量', type: 'line', smooth: true, data: [...histValues, ...predDates.map(() => null)], itemStyle: { color: '#3b82f6' } },
          { name: '预测销量', type: 'line', smooth: true, lineStyle: { type: 'dashed' }, data: [...histDates.map(() => null), ...predValues], itemStyle: { color: '#f59e0b' } },
          { name: '置信下限', type: 'line', smooth: true, symbol: 'none', lineStyle: { opacity: 0 }, data: [...histDates.map(() => null), ...lower], stack: 'confidence' },
          { name: '置信区间', type: 'line', smooth: true, symbol: 'none', lineStyle: { opacity: 0 }, areaStyle: { color: 'rgba(107,114,128,.16)' }, data: [...histDates.map(() => null), ...upper.map((v, i) => v - lower[i])], stack: 'confidence' }
        ]
      })
    },
    resizeChart() {
      if (this.chart) this.chart.resize()
    },
    exportResults() {
      const blob = new Blob([JSON.stringify({ analysis: this.analysisResults, prediction: this.predictionResults }, null, 2)], { type: 'application/json;charset=utf-8' })
      const url = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = url
      link.download = `sales_prediction_${Date.now()}.json`
      link.click()
      URL.revokeObjectURL(url)
    },
    formatNumber(value) {
      return Math.round(Number(value || 0)).toLocaleString()
    }
  }
}
</script>

<style scoped lang="scss">
.sales-page { padding: 20px; max-width: 1400px; margin: 0 auto; background: #f0f2f5; min-height: calc(100vh - 84px); color: #303133; }
.header-bar, .filter-bar, .chart-panel, .panel { background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,.06); }
.header-bar { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; margin-bottom: 20px; gap: 16px; flex-wrap: wrap; }
.header-title { margin: 0; font-size: 20px; font-weight: 600; }
.header-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.filter-bar { display: flex; align-items: flex-end; gap: 15px; padding: 20px 24px; margin-bottom: 20px; flex-wrap: wrap; }
.filter-group { display: flex; flex-direction: column; gap: 6px; min-width: 150px; }
.filter-group.wide { min-width: 240px; }
.filter-group label { font-size: 12px; color: #606266; font-weight: 500; }
.file-hint { margin-left: auto; display: flex; flex-wrap: wrap; gap: 12px; color: #67c23a; font-size: 13px; }
.progress { margin-bottom: 16px; }
.chart-panel { padding: 24px; margin-bottom: 20px; }
.chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; gap: 16px; flex-wrap: wrap; }
.chart-summary { display: flex; gap: 28px; }
.chart-summary div { display: flex; flex-direction: column; gap: 4px; }
.summary-label { color: #909399; font-size: 12px; }
.chart-summary strong { font-size: 24px; }
.up { color: #67c23a; }
.down { color: #f56c6c; }
.legend { display: flex; gap: 15px; color: #606266; font-size: 12px; flex-wrap: wrap; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { display: inline-block; }
.solid { width: 20px; height: 2px; }
.dashed { width: 20px; border-top: 2px dashed; }
.box { width: 12px; height: 12px; background: rgba(107,114,128,.2); border: 1px solid #9ca3af; }
.blue { background: #3b82f6; color: #3b82f6; }
.orange { color: #f59e0b; }
.gray { color: #9ca3af; }
.chart { height: 400px; }
.data-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; }
.panel { padding: 24px; min-width: 0; }
.panel h3 { margin: 0 0 18px; font-size: 16px; }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.panel-head h3 { margin: 0; }
.panel-head .el-input { max-width: 220px; }
.metric-panel { display: flex; flex-direction: column; gap: 15px; }
.metric-card { border-left: 4px solid #667eea; background: linear-gradient(135deg,#f8f9fa,#eef1f5); border-radius: 8px; padding: 18px; }
.metric-card span { display: block; color: #909399; font-size: 12px; margin-bottom: 8px; }
.metric-card strong { font-size: 24px; }
.metric-card.orange { border-left-color: #f59e0b; }
.metric-card.green { border-left-color: #67c23a; }
.metric-card.blue { border-left-color: #3b82f6; }
@media (max-width: 1100px) { .data-grid { grid-template-columns: 1fr; } .file-hint { margin-left: 0; } }
</style>
