<template>
  <div class="p-2 tou-page">
    <el-row :gutter="12">
      <el-col :xs="24" :md="7" :lg="6">
        <el-card shadow="hover" class="side-card">
          <template #header>
            <div class="side-header">
              <span>节点选择</span>
              <el-tag type="info" effect="plain">{{ selectedNodeIds.length }}</el-tag>
            </div>
          </template>
          <el-tree
            ref="treeRef"
            class="node-tree"
            :data="treeData"
            node-key="id"
            show-checkbox
            check-strictly
            default-expand-all
            :expand-on-click-node="false"
            :props="{ label: 'name', children: 'children' }"
            @check="handleTreeCheck"
          />
        </el-card>
      </el-col>

      <el-col :xs="24" :md="17" :lg="18">
        <el-card shadow="hover" class="filter-card">
          <el-form :inline="true" class="query-form">
            <el-form-item label="起止时间">
              <el-date-picker
                v-model="timeRange"
                type="datetimerange"
                value-format="YYYY-MM-DD HH:mm:ss"
                range-separator="至"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
                :default-time="defaultTime"
              />
            </el-form-item>
            <el-form-item label="时间周期">
              <el-select v-model="interval" placeholder="请选择时间周期" class="interval-select">
                <el-option v-for="item in intervalOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
            <el-form-item label="是否统一显示" label-width="100">
              <el-switch v-model="unified" active-text="是" inactive-text="否" inline-prompt @change="loadReport" />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" icon="Search" @click="loadReport">查询</el-button>
              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
            </el-form-item>
          </el-form>
        </el-card>

        <div v-loading="loading" class="charts-panel">
          <el-empty v-if="!loading && empty" description="请选择节点并查询平谷信息" />
          <div v-if="!empty && unified" class="chart-block">
            <div class="chart-title">
              <span>平谷信息汇总</span>
              <div class="summary">
                <span v-for="category in categories" :key="category" :class="`summary-item ${categoryClassMap[category]}`">
                  {{ category }} {{ formatEnergyFee(unifiedTotals[category], unifiedFees[category]) }}
                </span>
              </div>
            </div>
            <div :ref="(el) => setChartRef(el, unifiedChartKey)" class="tou-chart unified-chart"></div>
          </div>
          <div v-for="node in unified ? [] : reportNodes" :key="node.nodeId" class="chart-block">
            <div class="chart-title">
              <span>{{ node.name }}</span>
              <div class="summary">
                <span v-for="category in categories" :key="category" :class="`summary-item ${categoryClassMap[category]}`">
                  {{ category }} {{ formatEnergyFee(node.totals[category], node.totalsFee?.[category]) }}
                </span>
              </div>
            </div>
            <div :ref="(el) => setChartRef(el, String(node.nodeId))" class="tou-chart"></div>
          </div>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script setup name="TOUReport" lang="ts">
import * as echarts from 'echarts';
import { getTouReport } from '@/api/electricityManagement/TOU';
import { TouCategory, TouInterval, TouNodeSeries } from '@/api/electricityManagement/TOU/types';
import { listFloorInfo } from '@/api/system/floor';
import { FloorInfoVO } from '@/api/system/floor/types';

const treeRef = ref();
const loading = ref(false);
const treeData = ref<FloorInfoVO[]>([]);
const selectedNodeIds = ref<string[]>([]);
const reportNodes = ref<TouNodeSeries[]>([]);
const timeRange = ref<string[]>([]);
const interval = ref<TouInterval>('hour');
const unified = ref(false);
const defaultTime: [Date, Date] = [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)];
const unifiedChartKey = '__unified__';
const categories: TouCategory[] = ['峰', '平', '谷', '未定义'];
const categoryColorMap: Record<TouCategory, string> = {
  峰: '#e2553f',
  平: '#2f8f83',
  谷: '#2f6fbb',
  未定义: '#9aa3af'
};
const categoryClassMap: Record<TouCategory, string> = {
  峰: 'is-peak',
  平: 'is-flat',
  谷: 'is-valley',
  未定义: 'is-unknown'
};
const intervalOptions: Array<{ label: string; value: TouInterval }> = [
  { label: '分', value: 'minute' },
  { label: '时', value: 'hour' },
  { label: '日', value: 'day' },
  { label: '月', value: 'month' },
  { label: '年', value: 'year' },
  { label: '班组', value: 'shift' }
];
const chartRefs = new Map<string, HTMLDivElement>();
const chartInstances = new Map<string, echarts.ECharts>();

const empty = computed(() => !reportNodes.value.length);
const unifiedTotals = computed<Record<TouCategory, number>>(() => {
  return categories.reduce(
    (result, category) => {
      result[category] = reportNodes.value.reduce((sum, node) => sum + Number(node.totals[category] || 0), 0);
      return result;
    },
    { 峰: 0, 平: 0, 谷: 0, 未定义: 0 }
  );
});
const unifiedFees = computed<Record<TouCategory, number>>(() => {
  return categories.reduce(
    (result, category) => {
      result[category] = reportNodes.value.reduce((sum, node) => sum + Number(node.totalsFee?.[category] || 0), 0);
      return result;
    },
    { 峰: 0, 平: 0, 谷: 0, 未定义: 0 }
  );
});

function pad(value: number) {
  return String(value).padStart(2, '0');
}

function formatDateTime(date: Date) {
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}

function initDefaultRange() {
  const end = new Date();
  const start = new Date(end.getTime() - 24 * 60 * 60 * 1000);
  timeRange.value = [formatDateTime(start), formatDateTime(end)];
}

function isNormalNode(row: FloorInfoVO) {
  return row.isDevice !== '1' && row.isDeviceGroup !== '1';
}

function isElectricDevice(row: FloorInfoVO) {
  return row.isDevice === '1' && String(row.deviceType || '') === '2' && !!row.deviceId;
}

function normalizeSelectableTree(rows: FloorInfoVO[]): FloorInfoVO[] {
  const result: FloorInfoVO[] = [];
  rows.forEach((row) => {
    const children = normalizeSelectableTree(row.children || []);
    if (isNormalNode(row) || isElectricDevice(row)) {
      result.push({
        ...row,
        children
      });
    } else {
      result.push(...children);
    }
  });
  return result;
}

function firstSelectableIds(rows: FloorInfoVO[], limit = 3) {
  const ids: string[] = [];

  function walk(items: FloorInfoVO[]) {
    for (const item of items) {
      if (ids.length >= limit) return;
      ids.push(String(item.id));
      walk(item.children || []);
    }
  }

  walk(rows);
  return ids;
}

function handleTreeCheck() {
  selectedNodeIds.value = (treeRef.value?.getCheckedKeys?.() || []).map((item: string | number) => String(item));
}

function setChartRef(el: Element | ComponentPublicInstance | null, key: string) {
  if (el instanceof HTMLDivElement) {
    chartRefs.set(key, el);
  }
}

function formatEnergy(value: unknown) {
  const numberValue = Number(value || 0);
  return Number.isFinite(numberValue) ? `${numberValue.toFixed(3)} kWh` : '-';
}

function formatEnergyFee(energy: unknown, fee: unknown) {
  const energyValue = Number(energy || 0);
  const feeValue = Number(fee || 0);
  if (!Number.isFinite(energyValue) || !Number.isFinite(feeValue)) {
    return '-';
  }
  return `${energyValue.toFixed(3)}kWh/${feeValue.toFixed(2)}元`;
}

function renderCharts() {
  const activeKeys = new Set(unified.value && reportNodes.value.length ? [unifiedChartKey] : reportNodes.value.map((node) => String(node.nodeId)));
  chartInstances.forEach((chart, key) => {
    if (!activeKeys.has(key)) {
      chart.dispose();
      chartInstances.delete(key);
      chartRefs.delete(key);
    }
  });

  if (unified.value) {
    const el = chartRefs.get(unifiedChartKey);
    if (!el || !reportNodes.value.length) return;
    const chart = chartInstances.get(unifiedChartKey) || echarts.init(el, 'macarons');
    chartInstances.set(unifiedChartKey, chart);
    const firstNode = reportNodes.value[0];
    const xAxis = firstNode?.buckets.map((bucket) => bucket.label) || [];
    const series = reportNodes.value.flatMap((node) =>
      categories.map((category) => ({
        name: category,
        id: `${node.nodeId}_${category}`,
        type: 'bar',
        stack: String(node.nodeId),
        barGap: '20%',
        emphasis: { focus: 'series' },
        itemStyle: {
          color: categoryColorMap[category]
        },
        data: node.buckets.map((bucket) => ({
          value: Number(bucket[category] || 0),
          fee: Number(bucket.fees?.[category] || 0),
          nodeName: node.name
        }))
      }))
    );
    chart.setOption(
      {
        color: categories.map((category) => categoryColorMap[category]),
        tooltip: {
          trigger: 'axis',
          axisPointer: { type: 'shadow' },
          formatter: (params: any) => {
            const items = Array.isArray(params) ? params : [params];
            const title = items[0]?.axisValueLabel || '';
            const lines = items
              .filter((item: any) => Number(item.value || 0) !== 0)
              .map((item: any) => `${item.marker}${item.data?.nodeName || ''}-${item.seriesName}: ${formatEnergyFee(item.value, item.data?.fee)}`);
            return [title, ...lines].join('<br/>');
          }
        },
        legend: {
          type: 'scroll',
          top: 0,
          data: categories
        },
        grid: {
          top: 42,
          left: 58,
          right: 24,
          bottom: 48,
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: xAxis,
          axisLabel: {
            hideOverlap: true
          }
        },
        yAxis: {
          type: 'value',
          name: '用电量(kWh)'
        },
        dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20, bottom: 8 }],
        series
      },
      true
    );
    return;
  }

  reportNodes.value.forEach((node) => {
    const key = String(node.nodeId);
    const el = chartRefs.get(key);
    if (!el) return;
    const chart = chartInstances.get(key) || echarts.init(el, 'macarons');
    chartInstances.set(key, chart);
    const xAxis = node.buckets.map((bucket) => bucket.label);
    chart.setOption(
      {
        color: categories.map((category) => categoryColorMap[category]),
        tooltip: {
          trigger: 'axis',
          axisPointer: { type: 'shadow' },
          formatter: (params: any) => {
            const items = Array.isArray(params) ? params : [params];
            const title = items[0]?.axisValueLabel || '';
            const lines = items
              .filter((item: any) => Number(item.value || 0) !== 0)
              .map((item: any) => `${item.marker}${item.seriesName}: ${formatEnergyFee(item.value, item.data?.fee)}`);
            return [title, ...lines].join('<br/>');
          }
        },
        legend: {
          type: 'scroll',
          top: 0
        },
        grid: {
          top: 42,
          left: 58,
          right: 24,
          bottom: 48,
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: xAxis,
          axisLabel: {
            hideOverlap: true
          }
        },
        yAxis: {
          type: 'value',
          name: '用电量(kWh)'
        },
        dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20, bottom: 8 }],
        series: categories.map((category) => ({
          name: category,
          type: 'bar',
          stack: 'TOU',
          emphasis: { focus: 'series' },
          data: node.buckets.map((bucket) => ({
            value: Number(bucket[category] || 0),
            fee: Number(bucket.fees?.[category] || 0)
          }))
        }))
      },
      true
    );
  });
}

async function loadTree() {
  const res = await listFloorInfo();
  const rows = (res as any).rows || (res as any).data || [];
  treeData.value = normalizeSelectableTree(rows);
  selectedNodeIds.value = firstSelectableIds(treeData.value);
  await nextTick();
  treeRef.value?.setCheckedKeys?.(selectedNodeIds.value);
}

async function loadReport() {
  handleTreeCheck();
  if (!timeRange.value?.length || !selectedNodeIds.value.length) {
    reportNodes.value = [];
    renderCharts();
    return;
  }
  loading.value = true;
  try {
    const res = await getTouReport({
      nodeIds: selectedNodeIds.value.join(','),
      beginTime: timeRange.value[0],
      endTime: timeRange.value[1],
      interval: interval.value,
      unified: unified.value
    });
    reportNodes.value = res.data.nodes || [];
    await nextTick();
    renderCharts();
  } finally {
    loading.value = false;
  }
}

async function resetQuery() {
  initDefaultRange();
  interval.value = 'hour';
  unified.value = false;
  selectedNodeIds.value = firstSelectableIds(treeData.value);
  await nextTick();
  treeRef.value?.setCheckedKeys?.(selectedNodeIds.value);
  await loadReport();
}

function resizeCharts() {
  chartInstances.forEach((chart) => chart.resize());
}

onMounted(async () => {
  initDefaultRange();
  await loadTree();
  await loadReport();
  window.addEventListener('resize', resizeCharts);
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', resizeCharts);
  chartInstances.forEach((chart) => chart.dispose());
  chartInstances.clear();
  chartRefs.clear();
});
</script>

<style scoped>
.tou-page {
  min-height: 100%;
}

.side-card,
.filter-card {
  margin-bottom: 12px;
}

.side-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.node-tree {
  max-height: 760px;
  overflow: auto;
}

.query-form {
  margin-bottom: -8px;
}

.interval-select {
  width: 120px;
}

.charts-panel {
  min-height: 520px;
}

.chart-block {
  margin-bottom: 12px;
  padding: 14px 16px 10px;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: 6px;
}

.chart-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
  font-size: 15px;
  font-weight: 600;
}

.summary {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  font-size: 12px;
  font-weight: 400;
}

.summary-item {
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--el-fill-color-light);
}

.summary-item.is-peak {
  color: #c23b27;
}

.summary-item.is-flat {
  color: #167568;
}

.summary-item.is-valley {
  color: #245da2;
}

.summary-item.is-unknown {
  color: #667085;
}

.tou-chart {
  width: 100%;
  height: 340px;
}

.unified-chart {
  height: 420px;
}

@media (max-width: 900px) {
  .interval-select {
    width: 100%;
  }

  .chart-title {
    align-items: flex-start;
    flex-direction: column;
  }

  .summary {
    justify-content: flex-start;
  }
}
</style>
