<template>
  <section class="risk-observation-dashboard" :class="{ 'is-loading': showBlockingLoading }">
    <div v-if="showBlockingLoading" class="table-state risk-dashboard-loading-state" role="status" aria-live="polite">
      <TableLoadingState
        :title="loadingLabel"
        message="正在同步风险观察、风险等级和近期高风险记录"
        icon="mdi mdi-shield-search"
        floating
      />
    </div>
    <template v-else>
      <article class="panel dashboard-card risk-trend-panel">
        <div class="card-head">
          <h3>风险观察趋势 <i class="mdi mdi-information-outline"></i></h3>
          <div class="risk-window-controls">
            <span v-if="lastUpdatedLabel" class="risk-refresh-label">{{ lastUpdatedLabel }}</span>
            <span class="risk-window-label" aria-label="风险看板时间窗口">近 {{ dashboard.windowDays }} 天</span>
          </div>
        </div>
        <div v-if="loading" class="risk-dashboard-state">
          <i class="mdi mdi-loading mdi-spin"></i>
          <span>正在加载风险看板数据</span>
        </div>
        <div v-else-if="error" class="risk-dashboard-state error">
          <i class="mdi mdi-alert-circle-outline"></i>
          <span>{{ errorMessage }}</span>
        </div>
        <RiskDailyTrendChart v-else :rows="dailyRows" />
      </article>

      <article class="panel dashboard-card risk-level-panel">
      <div class="card-head">
        <h3>风险等级分布 <i class="mdi mdi-information-outline"></i></h3>
      </div>
      <DonutChart
        :items="levelLegend"
        :center-value="String(dashboard.totalObservations)"
        center-label="风险观察"
      />
      </article>

      <article class="panel dashboard-card risk-composition-panel">
      <div class="card-head">
        <h3>风险占比 <i class="mdi mdi-information-outline"></i></h3>
      </div>
      <DonutChart
        :items="sourceLegend"
        :center-value="String(dashboard.totalObservations)"
        center-label="风险项"
      />
      </article>

      <article class="panel dashboard-card risk-dimension-panel">
      <div class="card-head">
        <h3>业务维度分布 <i class="mdi mdi-information-outline"></i></h3>
      </div>
      <div class="risk-dimension-grid">
        <section
          v-for="group in dimensionGroups"
          :key="group.label"
          class="risk-dimension-group"
        >
          <span class="risk-dimension-title">{{ group.label }}</span>
          <div v-if="group.rows.length" class="risk-dimension-list">
            <div v-for="row in group.rows" :key="row.name" class="risk-dimension-row">
              <span>{{ row.name }}</span>
              <i><b :style="{ width: row.width }"></b></i>
              <strong>{{ row.count }}项</strong>
            </div>
          </div>
          <p v-else class="risk-dashboard-inline-empty">暂无数据</p>
        </section>
      </div>
      </article>

      <article class="panel dashboard-card risk-signal-panel">
      <div class="card-head">
        <h3>风险信号排行 <i class="mdi mdi-information-outline"></i></h3>
      </div>
      <BarChart
        v-if="signalRanking.length"
        :items="signalRanking"
        value-prefix=""
        value-suffix="项"
      />
      <div v-else class="risk-dashboard-empty">
        <i class="mdi mdi-shield-check-outline"></i>
        <span>当前周期暂无风险信号</span>
      </div>
      </article>

      <article class="panel dashboard-card risk-ranking-panel">
      <div class="card-head">
        <h3>异常排行 <i class="mdi mdi-information-outline"></i></h3>
      </div>
      <div v-if="rankingChartItems.length" class="risk-ranking-visual">
        <div class="risk-ranking-chart-block">
          <BarChart
            :items="rankingChartItems"
            value-prefix=""
            value-suffix="项"
            :compact="false"
          />
        </div>
        <div class="risk-ranking-detail-grid">
          <section
            v-for="group in rankingDetailGroups"
            :key="group.label"
            class="risk-ranking-group"
          >
            <span class="risk-ranking-title">{{ group.label }}</span>
            <ol class="risk-ranking-list">
              <li v-for="(row, index) in group.rows" :key="`${group.label}-${row.name}`">
                <em>{{ index + 1 }}</em>
                <div>
                  <strong>{{ row.name }}</strong>
                  <small>{{ row.amountLabel }}</small>
                </div>
                <span>{{ row.count }}项</span>
              </li>
            </ol>
          </section>
        </div>
      </div>
      <div v-else class="risk-dashboard-empty">
        <i class="mdi mdi-chart-bar"></i>
        <span>当前周期暂无异常排行</span>
      </div>
      </article>
    </template>
  </section>
</template>

<script setup>
import { computed } from 'vue'

import BarChart from '../charts/BarChart.vue'
import DonutChart from '../charts/DonutChart.vue'
import RiskDailyTrendChart from '../charts/RiskDailyTrendChart.vue'
import TableLoadingState from '../shared/TableLoadingState.vue'
import {
  formatRiskDimensionLabel
} from '../../utils/riskLabels.js'

const props = defineProps({
  dashboard: { type: Object, required: true },
  loading: { type: Boolean, default: false },
  error: { type: Object, default: () => null },
  levelLegend: { type: Array, default: () => [] },
  sourceLegend: { type: Array, default: () => [] },
  signalRanking: { type: Array, default: () => [] },
  dailyRows: { type: Array, default: () => [] },
  lastUpdatedAt: { type: String, default: '' }
})

const loadingLabel = computed(() => (
  props.lastUpdatedAt ? '正在同步最新风险数据' : '正在加载风险看板数据'
))
const showBlockingLoading = computed(() => props.loading && !props.lastUpdatedAt)
const lastUpdatedLabel = computed(() => {
  if (!props.lastUpdatedAt) {
    return ''
  }
  const date = new Date(props.lastUpdatedAt)
  if (Number.isNaN(date.getTime())) {
    return ''
  }
  return `上次同步 ${date.toLocaleTimeString('zh-CN', {
    hour12: false,
    hour: '2-digit',
    minute: '2-digit'
  })}`
})
const errorMessage = computed(() => props.error?.message || '风险看板数据加载失败')
const dimensionGroups = computed(() => [
  buildDimensionGroup('部门', props.dashboard.departmentDistribution, 'department'),
  buildDimensionGroup('费用类型', props.dashboard.expenseTypeDistribution, 'expense_type'),
  buildDimensionGroup('风险类型', props.dashboard.riskTypeDistribution, 'risk_type'),
  buildDimensionGroup('供应商', props.dashboard.supplierDistribution, 'supplier'),
  buildDimensionGroup('员工职级', props.dashboard.employeeGradeDistribution, 'grade')
])
const rankingGroups = computed(() => [
  buildRankingGroup('部门', props.dashboard.topDepartments, 'department'),
  buildRankingGroup('员工', props.dashboard.topEmployees, 'employee'),
  buildRankingGroup('供应商', props.dashboard.topSuppliers, 'supplier'),
  buildRankingGroup('规则', props.dashboard.topRules, 'rule'),
  buildRankingGroup('费用类型', props.dashboard.topExpenseTypes, 'expense_type')
])
const rankingChartItems = computed(() => rankingGroups.value
  .map((group, index) => {
    const topRow = group.rows[0]
    if (!topRow) {
      return null
    }
    return {
      name: group.label,
      shortName: group.label,
      value: topRow.count,
      meta: `${topRow.name} · ${topRow.amountLabel}`,
      color: [
        '#ef4444',
        '#f59e0b',
        'var(--theme-primary)',
        '#0f766e',
        '#2563eb'
      ][index] || '#64748b'
    }
  })
  .filter(Boolean))
const rankingDetailGroups = computed(() => rankingGroups.value
  .filter((group) => group.rows.length)
  .map((group) => ({
    ...group,
    rows: group.rows.slice(0, 3)
  })))

function buildDimensionGroup(label, distribution = {}, kind = '') {
  const rows = Object.entries(distribution || {})
    .map(([name, count]) => ({
      name: formatDimensionName(name, kind),
      count: Number(count || 0)
    }))
    .filter((item) => item.count > 0)
    .sort((a, b) => b.count - a.count)
    .slice(0, 4)
  const max = Math.max(...rows.map((item) => item.count), 1)

  return {
    label,
    rows: rows.map((item) => ({
      ...item,
      width: `${Math.max((item.count / max) * 100, 10)}%`
    }))
  }
}

function buildRankingGroup(label, rows = [], kind = '') {
  return {
    label,
    rows: (Array.isArray(rows) ? rows : [])
      .map((item) => ({
        name: formatDimensionName(item.name, kind),
        count: Number(item.count || 0),
        amount: Number(item.amount || 0),
        amountLabel: formatAmount(item.amount)
      }))
      .filter((item) => item.count > 0)
      .slice(0, 5)
  }
}

function formatAmount(value) {
  const amount = Number(value || 0)
  if (amount >= 1_000_000) return `¥${(amount / 1_000_000).toFixed(1)}M`
  if (amount >= 1_000) return `¥${(amount / 1_000).toFixed(1)}K`
  return `¥${Math.round(amount)}`
}

function formatDimensionName(value, kind = '') {
  return formatRiskDimensionLabel(value, kind)
}
</script>

<style scoped>
.risk-observation-dashboard {
  position: relative;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 18px;
  min-width: 0;
}

.risk-dashboard-loading-state {
  grid-column: 1 / -1;
  min-height: 0;
}

.dashboard-card {
  min-width: 0;
  padding: 18px;
  border: 1px solid #edf2f7;
  background: #fff;
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.card-head h3 {
  min-width: 0;
  color: #1e293b;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-head .mdi {
  color: #94a3b8;
  font-size: 12px;
}

.risk-window-label {
  flex: 0 0 auto;
  color: #64748b;
  font-size: 12px;
  font-weight: 700;
}

.risk-refresh-label {
  flex: 0 0 auto;
  color: #64748b;
  font-size: 12px;
  font-weight: 800;
}

.risk-window-controls {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

.risk-trend-panel,
.risk-signal-panel,
.risk-dimension-panel {
  grid-column: span 6;
}

.risk-ranking-panel {
  grid-column: span 12;
}

.risk-level-panel,
.risk-composition-panel {
  grid-column: span 3;
}

.risk-dashboard-state,
.risk-dashboard-empty {
  min-height: 220px;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 8px;
  color: #64748b;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
}

.risk-dashboard-state i,
.risk-dashboard-empty i {
  color: #94a3b8;
  font-size: 24px;
}

.risk-dashboard-state.error {
  color: #b91c1c;
}

.risk-dashboard-inline-empty {
  margin: 0;
  color: #94a3b8;
  font-size: 12px;
  font-weight: 700;
}

.risk-dimension-grid,
.risk-ranking-detail-grid {
  display: grid;
  gap: 10px;
}

.risk-dimension-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.risk-ranking-visual {
  display: grid;
  gap: 16px;
  min-height: 430px;
}

.risk-ranking-chart-block {
  min-width: 0;
  display: flex;
  min-height: 320px;
  padding: 2px 0 4px;
}

.risk-ranking-chart-block :deep(.bar-chart) {
  flex: 1;
  min-height: 320px;
}

.risk-ranking-chart-block :deep(.chart-area) {
  height: 320px;
}

.risk-ranking-detail-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: stretch;
}

.risk-dimension-group,
.risk-ranking-group {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 8px;
  padding: 12px;
  border: 1px solid #edf2f7;
  border-radius: 4px;
  background: #f8fafc;
}

.risk-ranking-group {
  min-height: 132px;
}

.risk-dimension-title,
.risk-ranking-title {
  color: #334155;
  font-size: 12px;
  font-weight: 900;
}

.risk-dimension-list {
  display: grid;
  gap: 8px;
}

.risk-dimension-row {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) 42px;
  align-items: center;
  gap: 8px;
}

.risk-dimension-row span,
.risk-dimension-row strong {
  min-width: 0;
  overflow: hidden;
  color: #64748b;
  font-size: 12px;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.risk-dimension-row strong {
  color: #0f172a;
  text-align: right;
}

.risk-dimension-row i {
  height: 7px;
  overflow: hidden;
  border-radius: 999px;
  background: #e2e8f0;
}

.risk-dimension-row b {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--theme-primary);
}

.risk-ranking-list {
  display: grid;
  gap: 7px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.risk-ranking-list li {
  min-width: 0;
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) 38px;
  align-items: center;
  gap: 8px;
}

.risk-ranking-list em {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: #e2e8f0;
  color: #475569;
  font-size: 11px;
  font-style: normal;
  font-weight: 900;
}

.risk-ranking-list div {
  min-width: 0;
  display: grid;
  gap: 2px;
}

.risk-ranking-list strong,
.risk-ranking-list small,
.risk-ranking-list span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.risk-ranking-list strong {
  color: #0f172a;
  font-size: 12px;
  font-weight: 850;
}

.risk-ranking-list small {
  color: #94a3b8;
  font-size: 11px;
  font-weight: 700;
}

.risk-ranking-list span {
  color: #64748b;
  font-size: 12px;
  font-weight: 850;
  text-align: right;
}

@media (max-width: 1280px) {
  .risk-trend-panel,
  .risk-signal-panel,
  .risk-dimension-panel,
  .risk-ranking-panel {
    grid-column: span 12;
  }

  .risk-level-panel,
  .risk-composition-panel {
    grid-column: span 6;
  }
}

@media (max-width: 760px) {
  .risk-observation-dashboard {
    grid-template-columns: minmax(0, 1fr);
  }

  .risk-trend-panel,
  .risk-signal-panel,
  .risk-dimension-panel,
  .risk-ranking-panel,
  .risk-level-panel,
  .risk-composition-panel {
    grid-column: 1;
  }

  .risk-dimension-grid,
  .risk-ranking-detail-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
</style>
