<template>
  <div class="app-container bi-dashboard" v-loading="loading">
    <el-card shadow="never" class="bi-toolbar-card">
      <div class="bi-toolbar">
        <div class="bi-toolbar__meta" v-if="hasData && dashboardData">
          <span>{{ biT("statDateHint", { date: dashboardData.statDate || biDash() }) }}</span>
          <span>{{ biT("statYearHint", { year: dashboardData.statYear }) }}</span>
        </div>
        <div class="bi-toolbar__actions">
          <span class="bi-toolbar__year-label">{{ biT("statYearLabel") }}</span>
          <el-select
            v-model="selectedStatYear"
            size="small"
            class="bi-toolbar__year"
            :disabled="loading || refreshing"
            @change="handleYearChange"
          >
            <el-option
              v-for="y in yearOptions"
              :key="y"
              :label="biT('yearOption', { year: y })"
              :value="y"
            />
          </el-select>
          <el-button
            type="primary"
            icon="el-icon-refresh"
            size="small"
            v-hasPermi="['dataModel:breedingIndustry:query']"
            :loading="refreshing"
            @click="handleRefresh"
          >
            {{ biT("btnRefresh") }}
          </el-button>
        </div>
      </div>
    </el-card>

    <el-card shadow="never" class="bi-section-card">
      <div slot="header" class="bi-section-header">{{ biT("sectionOverview") }}</div>
      <el-row :gutter="16">
        <el-col
          v-for="item in overviewItems"
          :key="item.key"
          :xs="12"
          :sm="8"
          :md="6"
          :lg="6"
          class="bi-overview-col"
        >
          <div class="bi-overview-item">
            <div class="bi-overview-item__value">{{ formatOverviewValue(item) }}</div>
            <div class="bi-overview-item__label">{{ biT(item.labelKey) }}</div>
          </div>
        </el-col>
      </el-row>
    </el-card>

    <el-card shadow="never" class="bi-section-card">
      <div slot="header" class="bi-section-header">{{ biT("sectionInventoryChange") }}</div>
      <el-row :gutter="16" class="bi-inventory-stats">
        <el-col :xs="24" :sm="12">
          <div class="bi-stat-block">
            <div class="bi-stat-block__value">{{ formatHead(inventoryChange.bullCount) }}</div>
            <div class="bi-stat-block__label">{{ biT("bullCount") }}</div>
          </div>
        </el-col>
        <el-col :xs="24" :sm="12">
          <div class="bi-stat-block">
            <div class="bi-stat-block__value">{{ formatHead(inventoryChange.cowCount) }}</div>
            <div class="bi-stat-block__label">{{ biT("cowCount") }}</div>
          </div>
        </el-col>
      </el-row>
      <div class="bi-chart-title">{{ biT("chartInventoryTrend") }}</div>
      <div ref="chartInventory" class="bi-chart-box" />
    </el-card>

    <el-row :gutter="16">
      <el-col :xs="24" :lg="12">
        <el-card shadow="never" class="bi-section-card">
          <div slot="header" class="bi-section-header">{{ biT("sectionYakInventoryStructure") }}</div>
          <div ref="chartYakStructure" class="bi-chart-box" />
        </el-card>
      </el-col>
      <el-col :xs="24" :lg="12">
        <el-card shadow="never" class="bi-section-card">
          <div slot="header" class="bi-section-header">{{ biT("sectionGrassland") }}</div>
          <el-row :gutter="16" class="bi-grass-stats">
            <el-col :span="12">
              <div class="bi-stat-block">
                <div class="bi-stat-block__value">{{ formatGe(grassland.availableCount) }}</div>
                <div class="bi-stat-block__label">{{ biT("grassAvailableCount") }}</div>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="bi-stat-block">
                <div class="bi-stat-block__value">{{ formatGe(grassland.inUseCount) }}</div>
                <div class="bi-stat-block__label">{{ biT("grassInUseCount") }}</div>
              </div>
            </el-col>
          </el-row>
          <div class="bi-chart-title">{{ biT("chartDegradation") }}</div>
          <el-row :gutter="16">
            <el-col :xs="24" :md="12">
              <div ref="chartDegradation" class="bi-chart-box bi-chart-box--sm" />
            </el-col>
            <el-col :xs="24" :md="12">
              <el-table :data="degradationTable" size="small" border stripe>
                <el-table-column prop="degradationLabel" :label="biT('colDegradationLevel')" min-width="100" />
                <el-table-column prop="count" :label="biT('colCount')" width="80" align="right">
                  <template slot-scope="scope">
                    {{ formatGe(scope.row.count, false) }}
                  </template>
                </el-table-column>
                <el-table-column prop="ratio" :label="biT('colRatio')" width="90" align="right">
                  <template slot-scope="scope">
                    {{ formatRatio(scope.row.ratio) }}
                  </template>
                </el-table-column>
              </el-table>
            </el-col>
          </el-row>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import * as echarts from "echarts"
require("echarts/theme/macarons")
import { debounce } from "@/utils"
import livestockIndustryLocaleMixin from "@/mixins/livestockIndustryLocaleMixin"
import { getBreedingDashboard } from "@/api/livestockIndustry/breedingIndustry"

const CHART_REFS = ["chartInventory", "chartYakStructure", "chartDegradation"]
const YEAR_START = 2020

function buildYearOptions(endYear = new Date().getFullYear()) {
  const end = Number(endYear)
  const start = YEAR_START
  if (!Number.isFinite(end) || end < start) {
    return [start]
  }
  const years = []
  for (let y = end; y >= start; y--) {
    years.push(y)
  }
  return years
}

export default {
  name: "BreedingIndustry",
  mixins: [livestockIndustryLocaleMixin],
  data() {
    const currentYear = new Date().getFullYear()
    return {
      biNs: "breedingIndustry",
      loading: false,
      refreshing: false,
      hasData: false,
      dashboardData: null,
      selectedStatYear: currentYear,
      yearOptions: buildYearOptions(currentYear),
      charts: {},
      resizeHandler: null
    }
  },
  computed: {
    overviewItems() {
      return [
        { key: "inventoryTotal", labelKey: "kpiInventoryTotal", unit: "unitHead", int: true },
        { key: "annualOutbound", labelKey: "kpiAnnualOutbound", unit: "unitHead", int: true },
        { key: "cooperativeCount", labelKey: "kpiCooperativeCount", unit: "unitGe", int: true },
        { key: "familyRanchCount", labelKey: "kpiFamilyRanchCount", unit: "unitGe", int: true },
        { key: "largeLivestockFarmerCount", labelKey: "kpiLargeLivestockFarmerCount", unit: "unitGe", int: true },
        { key: "pastureCount", labelKey: "kpiPastureCount", unit: "unitHousehold", int: true },
        { key: "withLivestockHouseholdCount", labelKey: "kpiWithLivestockHouseholdCount", unit: "unitHousehold", int: true },
        { key: "grasslandAreaMu", labelKey: "kpiGrasslandAreaMu", unit: "unitMu", int: false }
      ]
    },
    overview() {
      return (this.dashboardData && this.dashboardData.overview) || {}
    },
    inventoryChange() {
      return (this.dashboardData && this.dashboardData.inventoryChange) || {
        bullCount: 0,
        cowCount: 0,
        monthlyTrend: []
      }
    },
    yakInventoryStructure() {
      return (this.dashboardData && this.dashboardData.yakInventoryStructure) || []
    },
    grassland() {
      return (this.dashboardData && this.dashboardData.grassland) || {
        availableCount: 0,
        inUseCount: 0,
        degradationStats: []
      }
    },
    degradationTable() {
      return this.grassland.degradationStats || []
    }
  },
  mounted() {
    this.resizeHandler = debounce(() => this.resizeCharts(), 120)
    window.addEventListener("resize", this.resizeHandler)
    this.loadDashboard()
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.resizeHandler)
    this.disposeCharts()
  },
  methods: {
    formatOverviewValue(item) {
      if (item.fixedValue != null) {
        return item.fixedValue + " " + this.biCommon(item.unit)
      }
      if (!this.hasData) {
        return this.biDash()
      }
      const v = this.overview[item.key]
      if (v == null || v === "") {
        return item.int ? "0 " + this.biCommon(item.unit) : this.biDash()
      }
      if (item.int) {
        return v + " " + this.biCommon(item.unit)
      }
      return v + " " + this.biCommon(item.unit)
    },
    formatHead(val) {
      if (!this.hasData) {
        return this.biDash()
      }
      if (val == null || val === "") {
        return "0 " + this.biCommon("unitHead")
      }
      return val + " " + this.biCommon("unitHead")
    },
    formatGe(val, withUnit = true) {
      if (!this.hasData) {
        return this.biDash()
      }
      if (val == null || val === "") {
        return withUnit ? "0 " + this.biCommon("unitGe") : "0"
      }
      return withUnit ? val + " " + this.biCommon("unitGe") : String(val)
    },
    formatRatio(val) {
      if (!this.hasData || val == null || val === "") {
        return this.biDash()
      }
      return Number(val).toFixed(1) + "%"
    },
    loadDashboard(forceRefresh = false) {
      if (forceRefresh) {
        if (this.refreshing) {
          return
        }
        this.refreshing = true
      } else {
        this.loading = true
      }
      const params = {
        statYear: String(this.selectedStatYear)
      }
      if (forceRefresh) {
        params.forceRefresh = true
      }
      getBreedingDashboard(params)
        .then((res) => {
          this.dashboardData = res.data || null
          this.hasData = !!this.dashboardData
          if (this.hasData) {
            this.scheduleRenderCharts()
          } else {
            this.disposeCharts()
          }
        })
        .catch(() => {
          this.hasData = false
          this.dashboardData = null
          this.disposeCharts()
          this.$modal.msgError(this.biT("loadFailed"))
        })
        .finally(() => {
          this.loading = false
          this.refreshing = false
        })
    },
    handleYearChange() {
      this.loadDashboard(false)
    },
    handleRefresh() {
      this.loadDashboard(true)
    },
    scheduleRenderCharts(retry = 0) {
      this.$nextTick(() => {
        if (!this.$refs.chartInventory) {
          if (retry < 8) {
            this.scheduleRenderCharts(retry + 1)
          }
          return
        }
        this.renderCharts()
        this.$nextTick(() => this.resizeCharts())
      })
    },
    getChart(dom) {
      if (!dom) {
        return null
      }
      let chart = echarts.getInstanceByDom(dom)
      if (!chart) {
        chart = echarts.init(dom, "macarons")
      }
      return chart
    },
    disposeCharts() {
      CHART_REFS.forEach((ref) => {
        const dom = this.$refs[ref]
        if (!dom) {
          return
        }
        const inst = echarts.getInstanceByDom(dom)
        if (inst) {
          inst.dispose()
        }
      })
      this.charts = {}
    },
    resizeCharts() {
      Object.keys(this.charts).forEach((k) => {
        this.charts[k] && this.charts[k].resize()
      })
    },
    renderCharts() {
      this.renderInventoryChart()
      this.renderYakStructureChart()
      this.renderDegradationChart()
    },
    buildRollingTrendAxis(monthlyTrend) {
      const rows = [...(monthlyTrend || [])].sort((a, b) => {
        const ak = (Number(a.statYear) || 0) * 100 + (Number(a.month) || 0)
        const bk = (Number(b.statYear) || 0) * 100 + (Number(b.month) || 0)
        return ak - bk
      })
      return {
        rows,
        categories: rows.map((row) => this.biT("monthLabel", { m: Number(row.month) })),
        titleAt: (dataIndex) => {
          const row = rows[dataIndex]
          if (!row) {
            return ""
          }
          return `${row.statYear}年${Number(row.month)}月`
        }
      }
    },
    pickTrendCount(row, field) {
      const val = row?.[field]
      if (val == null || val === "") {
        return 0
      }
      const n = Number(val)
      return Number.isNaN(n) ? 0 : n
    },
    buildRollingTrendSeries(monthlyTrend, field) {
      const axis = this.buildRollingTrendAxis(monthlyTrend)
      return axis.rows.map((row) => this.pickTrendCount(row, field))
    },
    renderInventoryChart() {
      const dom = this.$refs.chartInventory
      const chart = this.getChart(dom)
      if (!chart) {
        return
      }
      const trend = this.inventoryChange.monthlyTrend || []
      if (!trend.length) {
        chart.setOption(
          {
            graphic: [
              {
                type: "text",
                left: "center",
                top: "middle",
                style: {
                  text: this.biT("emptyChart"),
                  fill: "#909399",
                  fontSize: 14
                }
              }
            ]
          },
          true
        )
        this.charts.inventory = chart
        return
      }
      const axis = this.buildRollingTrendAxis(trend)
      const bullData = this.buildRollingTrendSeries(trend, "bullCount")
      const cowData = this.buildRollingTrendSeries(trend, "cowCount")
      const hasPoints = bullData.some((v) => v > 0) || cowData.some((v) => v > 0)
      chart.setOption(
        {
          tooltip: {
            trigger: "axis",
            formatter: (params) => {
              if (!Array.isArray(params) || !params.length) {
                return ""
              }
              const title = axis.titleAt(params[0].dataIndex)
              const lines = params.map(
                (p) => `${p.marker}${p.seriesName}: ${p.value ?? 0} ${this.biCommon("unitHead")}`
              )
              return `${title}<br/>${lines.join("<br/>")}`
            }
          },
          legend: {
            top: 0,
            right: 8,
            data: [this.biT("seriesBullCount"), this.biT("seriesCowCount")]
          },
          grid: { left: "3%", right: "4%", bottom: "3%", top: 40, containLabel: true },
          xAxis: {
            type: "category",
            boundaryGap: false,
            data: axis.categories,
            axisLabel: { interval: 0, fontSize: 10 }
          },
          yAxis: {
            type: "value",
            name: this.biCommon("unitHead"),
            minInterval: 1
          },
          series: [
            {
              name: this.biT("seriesBullCount"),
              type: "line",
              smooth: true,
              symbol: "circle",
              symbolSize: 4,
              data: bullData
            },
            {
              name: this.biT("seriesCowCount"),
              type: "line",
              smooth: true,
              symbol: "circle",
              symbolSize: 4,
              data: cowData
            }
          ],
          graphic: hasPoints
            ? []
            : [
                {
                  type: "text",
                  left: "center",
                  top: "middle",
                  style: {
                    text: this.biT("emptyChart"),
                    fill: "#909399",
                    fontSize: 14
                  }
                }
              ]
        },
        true
      )
      this.charts.inventory = chart
    },
    renderYakStructureChart() {
      const dom = this.$refs.chartYakStructure
      const chart = this.getChart(dom)
      if (!chart) {
        return
      }
      const categories = this.yakInventoryStructure || []
      const innerData = categories.map((item) => ({
        name: item.categoryLabel,
        value: Number(item.count) || 0,
        ratio: item.ratio
      }))
      const outerData = []
      categories.forEach((cat) => {
        ;(cat.details || []).forEach((detail) => {
          outerData.push({
            name: detail.detailLabel,
            value: Number(detail.count) || 0,
            ratio: detail.ratio
          })
        })
      })
      const hasData = innerData.some((d) => d.value > 0) || outerData.some((d) => d.value > 0)
      chart.setOption(
        {
          tooltip: {
            trigger: "item",
            formatter: (p) => {
              const ratio =
                p.data && p.data.ratio != null ? this.formatRatio(p.data.ratio) : this.biDash()
              return `${p.seriesName}<br/>${p.marker}${p.name}: ${p.value ?? 0} ${this.biCommon(
                "unitHead"
              )} (${ratio})`
            }
          },
          legend: {
            type: "scroll",
            orient: "vertical",
            right: 8,
            top: "middle",
            data: [
              ...innerData.map((d) => d.name),
              ...outerData.map((d) => d.name).filter((name, idx, arr) => arr.indexOf(name) === idx)
            ]
          },
          series: [
            {
              name: this.biT("chartYakStructureInner"),
              type: "pie",
              radius: ["0%", "38%"],
              center: ["38%", "50%"],
              label: {
                position: "inner",
                fontSize: 11,
                formatter: (p) => {
                  const ratio =
                    p.data && p.data.ratio != null ? this.formatRatio(p.data.ratio) : ""
                  return `${p.name}\n${ratio}`
                }
              },
              data: hasData ? innerData : []
            },
            {
              name: this.biT("chartYakStructureOuter"),
              type: "pie",
              radius: ["48%", "68%"],
              center: ["38%", "50%"],
              label: {
                formatter: (p) => {
                  const ratio =
                    p.data && p.data.ratio != null ? this.formatRatio(p.data.ratio) : ""
                  return `${p.name}\n${ratio}`
                }
              },
              data: hasData ? outerData : []
            }
          ],
          graphic: hasData
            ? []
            : [
                {
                  type: "text",
                  left: "center",
                  top: "middle",
                  style: {
                    text: this.biT("emptyChart"),
                    fill: "#909399",
                    fontSize: 14
                  }
                }
              ]
        },
        true
      )
      this.charts.yakStructure = chart
    },
    renderDegradationChart() {
      const dom = this.$refs.chartDegradation
      const chart = this.getChart(dom)
      if (!chart) {
        return
      }
      const list = this.degradationTable
      const hasData = list.some((d) => d.count > 0)
      chart.setOption(
        {
          tooltip: {
            trigger: "item",
            formatter: (p) => {
              const item = list[p.dataIndex]
              const ratio = item && item.ratio != null ? this.formatRatio(item.ratio) : this.biDash()
              return `${p.name}<br/>${p.value} ${this.biCommon("unitGe")} (${ratio})`
            }
          },
          legend: {
            type: "scroll",
            bottom: 0
          },
          series: [
            {
              name: this.biT("chartDegradation"),
              type: "pie",
              radius: ["35%", "58%"],
              center: ["50%", "45%"],
              data: hasData
                ? list.map((item) => ({
                    name: item.degradationLabel,
                    value: item.count
                  }))
                : [],
              label: { show: false }
            }
          ],
          graphic: hasData
            ? []
            : [
                {
                  type: "text",
                  left: "center",
                  top: "middle",
                  style: {
                    text: this.biT("emptyChart"),
                    fill: "#909399",
                    fontSize: 14
                  }
                }
              ]
        },
        true
      )
      this.charts.degradation = chart
    }
  }
}
</script>

<style scoped lang="scss">
.bi-dashboard {
  padding-bottom: 16px;
}

.bi-toolbar-card {
  margin-bottom: 16px;

  ::v-deep .el-card__body {
    padding: 12px 16px;
  }
}

.bi-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
}

.bi-toolbar__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.bi-toolbar__year-label {
  color: #606266;
  font-size: 13px;
  white-space: nowrap;
}

.bi-toolbar__year {
  width: 110px;
}

.bi-toolbar__meta {
  margin-right: auto;
  color: #909399;
  font-size: 13px;

  span + span {
    margin-left: 16px;
  }
}

.bi-section-card {
  margin-bottom: 16px;
}

.bi-section-header {
  font-weight: 600;
  font-size: 15px;
}

.bi-overview-col {
  margin-bottom: 12px;
}

.bi-overview-item {
  padding: 16px 12px;
  text-align: center;
  background: #f8f9fb;
  border-radius: 4px;
  min-height: 88px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.bi-overview-item__value {
  font-size: 22px;
  font-weight: 600;
  color: #303133;
  line-height: 1.3;
  word-break: break-all;
}

.bi-overview-item__label {
  margin-top: 8px;
  font-size: 13px;
  color: #606266;
}

.bi-inventory-stats,
.bi-grass-stats {
  margin-bottom: 12px;
}

.bi-stat-block {
  padding: 12px 16px;
  background: #f8f9fb;
  border-radius: 4px;
  text-align: center;
  margin-bottom: 8px;
}

.bi-stat-block--hint {
  background: #fdf6ec;
}

.bi-stat-block__value {
  font-size: 20px;
  font-weight: 600;
  color: #303133;
}

.bi-stat-block__value--hint {
  font-size: 14px;
  font-weight: 500;
  color: #e6a23c;
}

.bi-stat-block__label {
  margin-top: 6px;
  font-size: 13px;
  color: #606266;
}

.bi-chart-title {
  margin: 8px 0 4px;
  font-size: 13px;
  color: #606266;
}

.bi-chart-box {
  width: 100%;
  height: 320px;
}

.bi-chart-box--sm {
  height: 280px;
}
</style>
