<template>
  <div ref="dashboardRef" class="sales-contract-dashboard" :class="{ 'is-fullscreen': isFullscreen }">
    <header class="dashboard-header">
      <div class="dashboard-header__filter">
        <span class="dashboard-header__filter-label">签订时间</span>
        <el-date-picker
          v-model="dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          range-separator="-"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :clearable="false"
          class="dashboard-header__date-picker"
          @change="handleDateChange"
        />
        <el-button type="primary" plain icon="Search" v-hasPermi="['oa/erp:salesContractDashboard:query']" @click="handleQuery">查询</el-button>
      </div>
      <h1 class="dashboard-header__title">销售合同仪表盘</h1>
      <div class="dashboard-header__actions">
        <el-button link type="primary" v-hasPermi="['oa/erp:salesContractDashboard:query']" @click="handleRefresh">
          <el-icon><Refresh /></el-icon>
          刷新
        </el-button>
        <el-button link type="primary" @click="toggleFullscreen">
          <el-icon><component :is="isFullscreen ? Close : FullScreen" /></el-icon>
          {{ isFullscreen ? '退出全屏' : '全屏' }}
        </el-button>
      </div>
    </header>

    <div v-loading="loading" class="dashboard-body">
      <!-- 顶部指标区 -->
      <section class="metrics-row">
        <div class="metric-card metric-card--summary">
          <div class="metric-card__accent" />
          <div class="metric-card__content">
            <div class="metric-card__value-wrap">
              <span class="metric-card__value">{{ formatAmountNumber(dashboardData.totalTransactionAmount) }}</span>
              <span class="metric-card__unit">元</span>
            </div>
            <div class="metric-card__label">合同成交总额</div>
          </div>
        </div>
        <div class="metric-card metric-card--summary">
          <div class="metric-card__accent" />
          <div class="metric-card__content">
            <div class="metric-card__value-wrap">
              <span class="metric-card__value">{{ formatAmountNumber(dashboardData.weeklyNewContractAmount) }}</span>
              <span class="metric-card__unit">元</span>
            </div>
            <div class="metric-card__label">本周新签合同额</div>
          </div>
        </div>
        <div class="metric-card metric-card--chart">
          <div class="metric-card__chart-header">
            <span class="metric-card__chart-title">业务方向销售额比例</span>
          </div>
          <div ref="ratioChartRef" class="metric-card__chart" />
        </div>
      </section>

      <!-- 底部图表区 -->
      <section class="charts-row">
        <div class="chart-panel">
          <div class="chart-panel__header">
            <span class="chart-panel__title">成交额客户分布</span>
            <span class="chart-panel__unit">单位：元</span>
          </div>
          <div ref="customerChartRef" class="chart-panel__body chart-panel__body--customer" :style="{ minHeight: `${customerChartHeight}px` }" />
        </div>
        <div class="chart-panel">
          <div class="chart-panel__header">
            <span class="chart-panel__title">销售合同额排行榜</span>
            <span class="chart-panel__unit">单位：元</span>
          </div>
          <div ref="rankingChartRef" class="chart-panel__body" />
        </div>
      </section>
    </div>
  </div>
</template>

<script setup lang="ts" name="SalesContractDashboard">
import { Close, FullScreen, Refresh } from '@element-plus/icons-vue';
import { useResizeObserver } from '@vueuse/core';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import { getSalesContractDashboard } from '@/api/oa/erp/salesContractDashboard';
import type { SalesContractDashboardVo } from '@/api/oa/erp/salesContractDashboard/types';
import { checkPermi } from '@/utils/permission';

const DASHBOARD_QUERY_PERM = ['oa/erp:salesContractDashboard:query'];

const { proxy } = getCurrentInstance() as ComponentInternalInstance;

const createEmptyDashboard = (): SalesContractDashboardVo => ({
  totalTransactionAmount: 0,
  weeklyNewContractAmount: 0,
  businessDirectionRatios: [],
  customerTransactionDistribution: [],
  salesContractRanking: []
});

const getCurrentYearRange = (): [string, string] => {
  const year = new Date().getFullYear();
  return [`${year}-01-01`, `${year}-12-31`];
};

const dashboardRef = ref<HTMLElement>();
const ratioChartRef = ref<HTMLElement>();
const customerChartRef = ref<HTMLElement>();
const rankingChartRef = ref<HTMLElement>();

const loading = ref(false);
const dateRange = ref<[string, string]>(getCurrentYearRange());
const dashboardData = ref<SalesContractDashboardVo>(createEmptyDashboard());

const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(dashboardRef);

let ratioChart: ECharts | null = null;
let customerChart: ECharts | null = null;
let rankingChart: ECharts | null = null;

const CHART_COLORS = {
  primary: '#3B76F1',
  ranking: '#B37FEB',
  /** 业务方向饼图配色（多色系、易区分） */
  ratioPalette: [
    '#3B76F1',
    '#5AD8A6',
    '#F6BD16',
    '#E86452',
    '#6DC8EC',
    '#945FB9',
    '#FF9845',
    '#1E9493',
    '#FF99C3',
    '#269A99',
    '#7585A2',
    '#F08BB4'
  ],
  palette: ['#3B76F1', '#5B8FF9', '#69C0FF', '#91D5FF', '#ADC6FF']
};

const AMOUNT_UNIT = '元';

const formatAmountNumber = (amount: number) => {
  return Number(amount || 0).toLocaleString('zh-CN', { maximumFractionDigits: 0 });
};

const formatAmount = (amount: number) => `${formatAmountNumber(amount)} ${AMOUNT_UNIT}`;

/** 根据类目文字长度估算 Y 轴左侧留白（中文约 13px/字） */
const calcCategoryGridLeft = (labels: string[]) => {
  const maxLen = labels.reduce((max, label) => Math.max(max, (label || '').length), 0);
  return Math.min(maxLen * 0.1, 10);
};

const customerChartHeight = computed(() => {
  const count = dashboardData.value.customerTransactionDistribution?.length || 0;
  return Math.max(320, count * 38 + 72);
});

/** 加载仪表盘数据 */
const loadDashboard = async () => {
  if (!dateRange.value?.length || dateRange.value.length !== 2) {
    proxy?.$modal.msgWarning('请选择签订时间区间');
    return;
  }
  loading.value = true;
  try {
    const res = await getSalesContractDashboard({
      beginDate: dateRange.value[0],
      endDate: dateRange.value[1]
    });
    dashboardData.value = res.data || createEmptyDashboard();
    await nextTick();
    renderCharts();
  } finally {
    loading.value = false;
  }
};

const handleDateChange = (value: [string, string] | null) => {
  if (value?.length === 2) {
    handleQuery();
  }
};

const handleQuery = () => {
  loadDashboard();
};

const buildRatioChartOption = (): echarts.EChartsOption => {
  const data = dashboardData.value.businessDirectionRatios || [];
  return {
    color: CHART_COLORS.ratioPalette,
    tooltip: {
      trigger: 'item',
      formatter: (params: any) => {
        const { name, value, percent } = params;
        return `${name}<br/>${formatAmount(value)} (${percent}%)`;
      }
    },
    legend: {
      orient: 'horizontal',
      bottom: 0,
      left: 'center',
      itemWidth: 10,
      itemHeight: 10,
      textStyle: { fontSize: 12, color: '#606266' }
    },
    series: [
      {
        name: '业务方向销售额比例',
        type: 'pie',
        radius: ['42%', '68%'],
        center: ['50%', '45%'],
        avoidLabelOverlap: true,
        label: { show: false },
        emphasis: {
          label: { show: true, fontSize: 14, fontWeight: 'bold' }
        },
        itemStyle: { borderRadius: 4 },
        data: data.map((item) => ({ name: item.name, value: item.value ?? 0 }))
      }
    ]
  };
};

const buildCustomerChartOption = (): echarts.EChartsOption => {
  const list = dashboardData.value.customerTransactionDistribution || [];
  const sorted = [...list].sort((a, b) => (a.amount ?? 0) - (b.amount ?? 0));
  const customerNames = sorted.map((item) => item.customerName || '未知客户');
  const gridLeft = calcCategoryGridLeft(customerNames);

  return {
    color: [CHART_COLORS.primary],
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      formatter: (params: any) => {
        const item = Array.isArray(params) ? params[0] : params;
        return `${item.axisValue}<br/>成交额: ${formatAmountNumber(item.value)}`;
      }
    },
    grid: { left: gridLeft, right: 56, top: 16, bottom: 16, containLabel: true },
    xAxis: {
      type: 'value',
      axisLabel: {
        formatter: (value: number) => formatAmountNumber(value)
      },
      splitLine: { lineStyle: { type: 'dashed', color: '#E4E7ED' } }
    },
    yAxis: {
      type: 'category',
      data: customerNames,
      axisTick: { show: false },
      axisLine: { lineStyle: { color: '#DCDFE6' } },
      axisLabel: {
        color: '#303133',
        fontSize: 12,
        interval: 0,
        hideOverlap: false,
        align: 'right',
        margin: 10
      }
    },
    series: [
      {
        name: '成交额',
        type: 'bar',
        barMaxWidth: 28,
        itemStyle: { borderRadius: [0, 6, 6, 0] },
        label: {
          show: true,
          position: 'right',
          color: '#606266',
          formatter: (params: any) => (params.value > 0 ? formatAmountNumber(params.value) : '')
        },
        data: sorted.map((item) => item.amount ?? 0)
      }
    ]
  };
};

const buildRankingChartOption = (): echarts.EChartsOption => {
  const list = dashboardData.value.salesContractRanking || [];
  const sorted = [...list].sort((a, b) => (a.contractAmount ?? 0) - (b.contractAmount ?? 0));

  return {
    color: [CHART_COLORS.ranking],
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      formatter: (params: any) => {
        const item = Array.isArray(params) ? params[0] : params;
        return `${item.axisValue}<br/>合同金额: ${formatAmountNumber(item.value)}`;
      }
    },
    legend: {
      bottom: 8,
      left: 'center',
      data: ['合同金额'],
      itemWidth: 12,
      itemHeight: 12,
      textStyle: { fontSize: 12, color: '#606266' }
    },
    grid: { left: 56, right: 24, top: 32, bottom: 48 },
    xAxis: {
      type: 'category',
      data: sorted.map((item) => item.salespersonName),
      axisTick: { alignWithLabel: true },
      axisLine: { lineStyle: { color: '#DCDFE6' } },
      axisLabel: { color: '#303133' }
    },
    yAxis: {
      type: 'value',
      axisLabel: {
        formatter: (value: number) => formatAmountNumber(value)
      },
      splitLine: { lineStyle: { type: 'dashed', color: '#E4E7ED' } }
    },
    series: [
      {
        name: '合同金额',
        type: 'bar',
        barMaxWidth: 48,
        itemStyle: { borderRadius: [6, 6, 0, 0] },
        label: {
          show: true,
          position: 'top',
          color: '#606266',
          formatter: (params: any) => formatAmountNumber(params.value)
        },
        data: sorted.map((item) => item.contractAmount ?? 0)
      }
    ]
  };
};

const initChart = (el: HTMLElement | undefined, theme?: string) => {
  if (!el) return null;
  return echarts.getInstanceByDom(el) ?? echarts.init(el, theme);
};

const renderCharts = () => {
  if (ratioChartRef.value) {
    ratioChart = initChart(ratioChartRef.value);
    ratioChart?.setOption(buildRatioChartOption(), true);
  }
  if (customerChartRef.value) {
    customerChart = initChart(customerChartRef.value);
    customerChart?.setOption(buildCustomerChartOption(), true);
  }
  if (rankingChartRef.value) {
    rankingChart = initChart(rankingChartRef.value);
    rankingChart?.setOption(buildRankingChartOption(), true);
  }
};

const resizeCharts = () => {
  ratioChart?.resize();
  customerChart?.resize();
  rankingChart?.resize();
};

const disposeCharts = () => {
  ratioChart?.dispose();
  customerChart?.dispose();
  rankingChart?.dispose();
  ratioChart = null;
  customerChart = null;
  rankingChart = null;
};

const handleRefresh = () => {
  loadDashboard();
};

watch(isFullscreen, () => {
  nextTick(() => resizeCharts());
});

watch(customerChartHeight, () => {
  nextTick(() => customerChart?.resize());
});

useResizeObserver(dashboardRef, () => {
  resizeCharts();
});

onMounted(() => {
  loadDashboard();
});

onBeforeUnmount(() => {
  disposeCharts();
});
</script>

<style lang="scss" scoped>
.sales-contract-dashboard {
  min-height: calc(100vh - 84px);
  padding: 16px 20px 24px;
  background: linear-gradient(180deg, #eef2f8 0%, #f5f7fa 120px, #f5f7fa 100%);
  box-sizing: border-box;

  &.is-fullscreen {
    min-height: 100vh;
    padding: 20px 28px 28px;
    overflow: auto;
  }
}

.dashboard-header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  padding: 14px 20px;
  background: #fff;
  border-radius: 10px;
  border: 1px solid #ebeef5;
  box-shadow: 0 2px 12px rgba(31, 45, 61, 0.06);

  &__filter {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
  }

  &__filter-label {
    font-size: 14px;
    font-weight: 500;
    color: #606266;
    white-space: nowrap;
  }

  &__date-picker {
    width: 280px;
  }

  &__title {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
    color: #1f2d3d;
    text-align: center;
    letter-spacing: 2px;
  }

  &__actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
  }
}

.dashboard-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.metrics-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1.5fr;
  gap: 18px;

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

.metric-card {
  position: relative;
  overflow: hidden;
  min-height: 168px;
  background: #fff;
  border-radius: 10px;
  border: 1px solid #ebeef5;
  box-shadow: 0 2px 12px rgba(31, 45, 61, 0.05);
  transition: transform 0.2s ease, box-shadow 0.2s ease;

  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(31, 45, 61, 0.08);
  }

  &--summary {
    display: flex;
    flex-direction: column;
  }

  &__accent {
    height: 4px;
    background: linear-gradient(90deg, #e75e58 0%, #f08984 100%);
  }

  &__content {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 28px 20px 24px;
  }

  &__value-wrap {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 6px;
    flex-wrap: wrap;
  }

  &__value {
    font-size: 38px;
    font-weight: 700;
    line-height: 1.1;
    color: #e75e58;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
  }

  &__unit {
    font-size: 18px;
    font-weight: 600;
    color: #e75e58;
    opacity: 0.85;
  }

  &__label {
    margin-top: 14px;
    font-size: 15px;
    font-weight: 500;
    color: #606266;
  }

  &--chart {
    display: flex;
    flex-direction: column;
    padding: 16px 18px 12px;
  }

  &__chart-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 4px;
    padding-bottom: 10px;
    border-bottom: 1px solid #f0f2f5;
  }

  &__chart-title {
    font-size: 15px;
    font-weight: 600;
    color: #303133;
  }

  &__chart-unit {
    font-size: 12px;
    color: #909399;
    white-space: nowrap;
  }

  &__chart {
    flex: 1;
    width: 100%;
    min-height: 132px;
  }
}

.charts-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;

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

.chart-panel {
  display: flex;
  flex-direction: column;
  min-height: 400px;
  padding: 0 18px 16px;
  background: #fff;
  border-radius: 10px;
  border: 1px solid #ebeef5;
  box-shadow: 0 2px 12px rgba(31, 45, 61, 0.05);
  transition: box-shadow 0.2s ease;

  &:hover {
    box-shadow: 0 8px 20px rgba(31, 45, 61, 0.08);
  }

  &__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 0 12px;
    margin-bottom: 4px;
    border-bottom: 1px solid #f0f2f5;
  }

  &__title {
    position: relative;
    padding-left: 12px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;

    &::before {
      content: '';
      position: absolute;
      left: 0;
      top: 50%;
      width: 4px;
      height: 16px;
      border-radius: 2px;
      background: var(--el-color-primary);
      transform: translateY(-50%);
    }
  }

  &__unit {
    font-size: 12px;
    color: #909399;
    white-space: nowrap;
  }

  &__body {
    flex: 1;
    min-height: 320px;

    &--customer {
      min-height: 320px;
    }
  }
}
</style>
