<template>
  <div class="dashboard-screen">
    <section class="summary-row" aria-label="核心指标">
      <article v-for="item in topMetrics" :key="item.label" class="summary-card" :class="item.tone">
        <div class="summary-icon" :class="item.iconKind" aria-hidden="true">
          <span></span>
        </div>
        <div class="summary-content">
          <span>{{ item.label }}</span>
          <div class="summary-number">
            <strong>{{ item.value }}</strong>
            <em>{{ item.unit }}</em>
          </div>
          <p>
            {{ item.subLabel }}
            <b :class="item.changeTone">{{ item.change }}</b>
          </p>
        </div>
      </article>
    </section>
    <div v-if="dashboardError" class="dashboard-error">{{ dashboardError }}</div>

    <section class="command-grid">
      <aside class="side-column left-column">
        <article class="screen-panel device-panel">
          <header class="panel-header">
            <h2>设备类型占比</h2>
          </header>
          <div class="donut-layout">
            <div ref="deviceTypeChartRef" class="chart donut-chart"></div>
            <ul class="legend-list">
              <li v-for="item in deviceTypeStats" :key="item.name">
                <span :style="{ background: item.color }"></span>
                <em>{{ item.name }}</em>
                <strong>{{ item.percent }}</strong>
              </li>
            </ul>
          </div>
        </article>

        <article class="screen-panel status-panel">
          <header class="panel-header">
            <h2>设备运行状态</h2>
            <small>单位：台</small>
          </header>
          <div ref="statusChartRef" class="chart status-chart"></div>
        </article>

        <article class="screen-panel trend-panel">
          <header class="panel-header">
            <h2>城市接入趋势<span>近12个月</span></h2>
          </header>
          <div ref="trendChartRef" class="chart trend-chart"></div>
        </article>

        <article class="screen-panel base-panel">
          <header class="panel-header">
            <h2>设备基础信息</h2>
          </header>
          <div class="base-grid">
            <div v-for="item in baseInfos" :key="item.label" class="base-item" :class="item.kind">
              <i aria-hidden="true"></i>
              <span>{{ item.label }}</span>
              <strong>{{ item.value }}</strong>
            </div>
          </div>
        </article>
      </aside>

      <main class="center-column">
        <article class="screen-panel map-panel">
          <header class="panel-header">
            <h2>全国城市接入分布</h2>
          </header>
          <div class="map-stage">
            <div ref="overviewMapRef" class="overview-amap" :class="{ visible: overviewMapReady }"></div>
            <div class="map-legend">
              <span>图例（设备数/个）</span>
              <i class="c5"></i><em>&gt; 5000</em>
              <i class="c4"></i><em>2000 - 5000</em>
              <i class="c3"></i><em>1000 - 2000</em>
              <i class="c2"></i><em>500 - 1000</em>
              <i class="c1"></i><em>&lt; 500</em>
            </div>
            <svg v-show="!overviewMapReady" class="china-map" viewBox="0 0 820 520" preserveAspectRatio="xMidYMid meet" role="img" aria-label="全国城市接入网络">
              <defs>
                <linearGradient id="mapFill" x1="0" y1="0" x2="1" y2="1">
                  <stop offset="0%" stop-color="#0c3d85" />
                  <stop offset="56%" stop-color="#052a63" />
                  <stop offset="100%" stop-color="#031b43" />
                </linearGradient>
                <filter id="mapGlow" x="-30%" y="-30%" width="160%" height="160%">
                  <feGaussianBlur stdDeviation="4" result="blur" />
                  <feMerge>
                    <feMergeNode in="blur" />
                    <feMergeNode in="SourceGraphic" />
                  </feMerge>
                </filter>
              </defs>
              <g class="map-body" transform="translate(50 22) scale(0.88)">
                <path
                  class="map-outline mainland"
                  d="M82 221 C64 190 91 163 124 169 C137 131 176 123 205 100 C233 74 262 84 286 62 C321 86 351 55 388 75 C423 48 468 67 506 57 C548 48 571 84 604 88 C634 90 652 112 668 141 C711 142 744 165 754 201 C786 219 764 257 780 285 C760 308 737 316 720 349 C700 386 660 374 632 405 C608 436 573 428 540 456 C503 489 462 463 428 489 C385 505 359 462 317 468 C277 474 253 432 211 426 C169 420 152 388 114 386 C91 358 111 326 79 304 C48 278 48 240 82 221 Z"
                />
                <path
                  class="map-outline hainan"
                  d="M490 462 C509 445 541 449 557 468 C546 488 514 493 493 478 C487 473 486 467 490 462 Z"
                />
                <path
                  class="map-outline taiwan"
                  d="M704 365 C724 385 729 427 710 455 C689 434 686 391 704 365 Z"
                />
                <path class="province-line" d="M132 135 C197 172 262 169 323 149 S434 120 509 93" />
                <path class="province-line" d="M99 187 C192 211 282 223 382 211 S585 203 715 237" />
                <path class="province-line" d="M124 276 C220 288 311 306 397 304 S568 296 704 325" />
                <path class="province-line" d="M210 424 C260 360 301 325 360 306 C436 268 512 263 608 382" />
                <path class="province-line" d="M288 68 C324 166 330 247 318 463" />
                <path class="province-line" d="M451 62 C432 150 432 234 456 393" />
                <path class="province-line" d="M594 112 C552 190 541 280 540 456" />
                <path class="province-line" d="M668 141 C641 196 629 254 632 405" />
                <g class="map-lines">
                  <path v-for="line in mapLines" :key="`${line.from.name}-${line.to.name}`" :d="linePath(line)" />
                </g>
                <g v-for="city in mapCities" :key="city.name" class="map-city" :class="city.level" :transform="`translate(${city.x} ${city.y})`">
                  <circle class="pulse" r="14" />
                  <circle class="halo" r="8" />
                  <circle class="dot" r="4" />
                  <text :x="city.labelX" :y="city.labelY">{{ city.name }}</text>
                </g>
              </g>
            </svg>
            <div class="south-sea">南海诸岛</div>
          </div>
        </article>

        <article class="screen-panel overview-panel">
          <header class="panel-header">
            <h2>区域设备概览</h2>
            <button type="button">更多 &gt;</button>
          </header>
          <div class="overview-table">
            <div class="table-head">
              <span>区域名称</span>
              <span>设备总数</span>
              <span>在线设备</span>
              <span>在线率</span>
              <span>未处理告警</span>
              <span>经度</span>
              <span>纬度</span>
            </div>
            <div v-for="row in projectOverview" :key="row.regionId" class="table-row">
              <span>{{ row.regionName }}</span>
              <span>{{ formatNumber(row.total) }}</span>
              <span>{{ formatNumber(row.online) }}</span>
              <strong>{{ formatPercent(row.onlineRate) }}</strong>
              <em>{{ row.alarms }}</em>
              <span>{{ row.longitude ?? '-' }}</span>
              <span>{{ row.latitude ?? '-' }}</span>
            </div>
          </div>
        </article>
      </main>

      <aside class="side-column right-column">
        <article class="screen-panel alarm-panel">
          <header class="panel-header">
            <h2>故障告警信息</h2>
            <button type="button">更多 &gt;</button>
          </header>
          <div class="alarm-table">
            <div class="alarm-head">
              <span>城市</span>
              <span>告警名称</span>
              <span>设备编码</span>
              <span>告警类型</span>
              <span>告警时间</span>
              <span>状态</span>
            </div>
            <div v-for="alarm in alarmRows" :key="alarm.id" class="alarm-row">
              <strong>{{ alarm.city }}</strong>
              <span>{{ alarm.title }}</span>
              <span>{{ alarm.code }}</span>
              <span>{{ alarm.type }}</span>
              <time>{{ alarm.time }}</time>
              <b :class="alarm.statusClass">{{ alarm.statusText }}</b>
            </div>
          </div>
        </article>

        <article class="screen-panel rank-panel">
          <header class="panel-header">
            <h2>区域排行<span>按设备数量</span></h2>
            <small>单位：台</small>
          </header>
          <div class="rank-list">
            <div v-for="(item, index) in projectRank" :key="item.name" class="rank-row">
              <i :class="{ top: index < 3 }">{{ index + 1 }}</i>
              <span>{{ item.name }}</span>
              <div class="rank-bar">
                <em :style="{ width: `${item.percent}%` }"></em>
              </div>
              <strong>{{ formatNumber(item.count) }}</strong>
            </div>
          </div>
        </article>

        <article class="screen-panel energy-panel">
          <header class="panel-header">
            <h2>能耗与节能分析<span>本月</span></h2>
          </header>
          <div class="energy-cards">
            <div>
              <span>本月用电量</span>
              <strong>{{ formatNumber(mockEnergy.monthUsage) }}<em>kWh</em></strong>
              <b>环比 ↓ {{ mockEnergy.usageDrop }}%</b>
            </div>
            <div>
              <span>节能率</span>
              <strong>{{ mockEnergy.savingRate }}%</strong>
              <b>较上月 ↑ {{ mockEnergy.savingIncrease }}%</b>
            </div>
            <div>
              <span>累计节电量</span>
              <strong>{{ formatNumber(mockEnergy.savedPower) }}<em>kWh</em></strong>
              <b>约等于植树 {{ formatNumber(mockEnergy.treeEquivalent) }} 棵</b>
            </div>
          </div>
          <div ref="energyChartRef" class="chart energy-chart"></div>
        </article>
      </aside>
    </section>
  </div>
</template>

<script setup lang="ts">
import * as echarts from 'echarts';
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
import { dashboardApi } from '../../api/modules';
import { getMapConfig, loadAmap } from '../../utils/amap';

interface CountGroup {
  level?: string;
  status?: string;
  _count: number | Record<string, number>;
}

interface RecentAlarm {
  id: string;
  title: string;
  type: string;
  level: string;
  status: string;
  occurredAt: string;
  lampName: string;
  lampCode: string;
  deviceType: string;
  regionName: string;
}

interface RegionRanking {
  regionId: string;
  regionName: string;
  count: number;
}

interface DeviceTypeStat {
  name: string;
  count: number;
}

interface DeviceBaseStats {
  lamp: number;
  terminal: number;
  distributionBox: number;
  camera: number;
  sensor: number;
  energyCollector: number;
}

interface AccessTrendItem {
  month: string;
  accessCityCount: number;
  deviceCount: number;
}

interface RegionDeviceRow {
  regionId: string;
  regionName: string;
  total: number;
  online: number;
  onlineRate: number;
  alarms: number;
  longitude: number | null;
  latitude: number | null;
}

interface MetricChanges {
  accessCityCount: number;
  accessRegionCount: number;
  assetTotal: number;
}

interface DashboardSummary {
  accessCityCount: number;
  accessRegionCount: number;
  metricChanges: MetricChanges;
  projectTotal: number | null;
  hasProjectTable: boolean;
  hasEnergyTable: boolean;
  assetTotal: number;
  lampTotal: number;
  terminalTotal: number;
  onlineTotal: number;
  lampOnlineTotal: number;
  terminalOnlineTotal: number;
  onlineRate: number;
  lampOnlineRate: number;
  terminalOnlineRate: number;
  openAlarms: number;
  todayAlarms: number;
  criticalAlarms: number;
  alarmsByStatus: CountGroup[];
  alarmsByLevel: CountGroup[];
  lampsByStatus: CountGroup[];
  terminalsByStatus: CountGroup[];
  deviceTypeStats: DeviceTypeStat[];
  deviceBaseStats: DeviceBaseStats;
  accessTrend: AccessTrendItem[];
  regionDeviceRanking: RegionDeviceRow[];
  mapRegions: RegionDeviceRow[];
  energy: null;
  recentAlarms: RecentAlarm[];
  recentAlarmsTotal: number;
  regionRanking: RegionRanking[];
}

interface CityNode {
  name: string;
  x: number;
  y: number;
  labelX: number;
  labelY: number;
  level: string;
}

interface MapLine {
  from: CityNode;
  to: CityNode;
}

const deviceTypeChartRef = ref<HTMLDivElement | null>(null);
const statusChartRef = ref<HTMLDivElement | null>(null);
const trendChartRef = ref<HTMLDivElement | null>(null);
const energyChartRef = ref<HTMLDivElement | null>(null);
const overviewMapRef = ref<HTMLDivElement | null>(null);
const overviewMapReady = ref(false);
const overviewMapInstance = ref<any>();
const overviewMapOverlays = ref<any[]>([]);
const summary = ref<DashboardSummary>(createEmptySummary());
const dashboardError = ref('');
const charts = new Map<string, echarts.ECharts>();
const mockEnergy = {
  monthUsage: 12532560,
  usageDrop: 8.35,
  savingRate: 23.68,
  savingIncrease: 3.25,
  savedPower: 2969120,
  treeEquivalent: 1245,
};
const energyDays = Array.from({ length: 30 }, (_, index) => `05-${String(index + 1).padStart(2, '0')}`);
const energyUse = [620, 760, 590, 870, 1010, 930, 840, 610, 560, 690, 780, 720, 640, 860, 730, 920, 680, 990, 610, 760, 930, 720, 850, 640, 900, 720, 610, 840, 730, 560];
const savingRate = [36, 34, 38, 43, 41, 42, 39, 37, 40, 45, 43, 41, 38, 42, 46, 43, 45, 41, 43, 42, 45, 39, 42, 38, 43, 41, 39, 44, 42, 40];

const topMetrics = computed(() => {
  const assetTotal = summary.value.assetTotal;
  const onlineTotal = summary.value.onlineTotal;
  const offlineTotal = Math.max(assetTotal - onlineTotal, 0);
  const offlineRate = assetTotal > 0 ? Math.max(0, 100 - summary.value.onlineRate) : 0;

  return [
    { label: '接入城市', value: formatNumber(summary.value.accessCityCount), unit: '个', subLabel: '较昨日', change: formatSigned(summary.value.metricChanges.accessCityCount), changeTone: 'up', tone: 'blue', iconKind: 'city' },
    { label: '接入区域', value: formatNumber(summary.value.accessRegionCount), unit: '个', subLabel: '较昨日', change: formatSigned(summary.value.metricChanges.accessRegionCount), changeTone: 'up', tone: 'blue', iconKind: 'project' },
    { label: '设备总数', value: formatNumber(assetTotal), unit: '台', subLabel: '较昨日', change: formatSigned(summary.value.metricChanges.assetTotal), changeTone: 'up', tone: 'blue', iconKind: 'device' },
    { label: '在线设备', value: formatNumber(onlineTotal), unit: '台', subLabel: '在线率', change: `${formatPercent(summary.value.onlineRate)}`, changeTone: 'neutral', tone: 'cyan', iconKind: 'online' },
    { label: '离线设备', value: formatNumber(offlineTotal), unit: '台', subLabel: '离线率', change: `${formatPercent(offlineRate)}`, changeTone: 'neutral', tone: 'orange', iconKind: 'offline' },
    { label: '今日告警', value: formatNumber(summary.value.todayAlarms), unit: '条', subLabel: '未处理', change: formatNumber(summary.value.openAlarms), changeTone: 'danger', tone: 'red', iconKind: 'alarm' },
  ];
});

const deviceTypeStats = computed(() => {
  const colors = ['#2f8cff', '#30d5e8', '#43ddae', '#ffbd3f', '#7d8dff', '#9aa8c3'];
  const total = Math.max(summary.value.assetTotal, 1);
  return summary.value.deviceTypeStats.map((item, index) => ({
    name: item.name,
    value: item.count,
    color: colors[index % colors.length],
    percent: `${((item.count / total) * 100).toFixed(2)}%`,
  }));
});

const runStatus = computed(() => {
  const assetTotal = summary.value.assetTotal;
  const onlineTotal = summary.value.onlineTotal;
  const offlineTotal = Math.max(assetTotal - onlineTotal, 0);
  return [
    { label: '正常', value: Math.max(onlineTotal - summary.value.openAlarms, 0), color: '#50e0a4' },
    { label: '故障', value: Math.max(summary.value.criticalAlarms, 0), color: '#f6b23b' },
    { label: '告警', value: summary.value.openAlarms, color: '#ff6b4a' },
    { label: '离线', value: offlineTotal, color: '#3f8cff' },
  ];
});

const baseInfos = computed(() => [
  { label: '单灯总数', value: formatNumber(summary.value.deviceBaseStats.lamp), kind: 'pole' },
  { label: '终端总数', value: formatNumber(summary.value.deviceBaseStats.terminal), kind: 'controller' },
  { label: '配电箱总数', value: formatNumber(summary.value.deviceBaseStats.distributionBox), kind: 'box' },
  { label: '摄像头数量', value: formatNumber(summary.value.deviceBaseStats.camera), kind: 'camera' },
  { label: '传感器数量', value: formatNumber(summary.value.deviceBaseStats.sensor), kind: 'sensor' },
  { label: '能耗采集点', value: formatNumber(summary.value.deviceBaseStats.energyCollector), kind: 'energy' },
]);

const projectRank = computed(() => {
  const rows = summary.value.regionDeviceRanking.slice(0, 8);
  const max = Math.max(...rows.map((item) => item.total), 1);
  return rows.map((item) => ({
    name: item.regionName,
    count: item.total,
    percent: Math.max(8, Math.round((item.total / max) * 100)),
  }));
});

const projectOverview = computed(() => summary.value.regionDeviceRanking.slice(0, 8));

const alarmRows = computed(() => {
  return summary.value.recentAlarms.slice(0, 7).map((alarm) => ({
    id: alarm.id,
    city: alarm.regionName.replace(/市.*$/, '市'),
    title: alarm.title,
    code: alarm.lampCode || alarm.lampName,
    type: alarm.type || alarm.deviceType || '设备告警',
    time: formatTimeShort(alarm.occurredAt),
    statusText: alarmStatusLabel(alarm.status),
    statusClass: alarm.status === 'OPEN' ? 'danger' : alarm.status === 'PROCESSING' ? 'warning' : 'success',
  }));
});

const trendMonths = computed(() => summary.value.accessTrend.map((item) => item.month));
const accessTrend = computed(() => summary.value.accessTrend.map((item) => item.accessCityCount));
const deviceTrend = computed(() => summary.value.accessTrend.map((item) => item.deviceCount));
const mapGeoCities = computed(() =>
  summary.value.mapRegions
    .filter((region) => Number.isFinite(Number(region.longitude)) && Number.isFinite(Number(region.latitude)))
    .map((region, index) => ({
      name: region.regionName,
      lng: Number(region.longitude),
      lat: Number(region.latitude),
      level: index < 2 ? 'hot' : 'major',
    })),
);
const mapCities = computed<CityNode[]>(() =>
  mapGeoCities.value.map((city) => ({
    name: city.name,
    ...projectLngLat(city.lng, city.lat),
    labelX: 10,
    labelY: -12,
    level: city.level,
  })),
);
const mapLines = computed<MapLine[]>(() => {
  const [hub, ...rest] = mapCities.value;
  if (!hub) {
    return [];
  }
  return rest.map((city) => ({ from: hub, to: city }));
});

function createEmptySummary(): DashboardSummary {
  return {
    accessCityCount: 0,
    accessRegionCount: 0,
    metricChanges: { accessCityCount: 0, accessRegionCount: 0, assetTotal: 0 },
    projectTotal: null,
    hasProjectTable: false,
    hasEnergyTable: false,
    assetTotal: 0,
    lampTotal: 0,
    terminalTotal: 0,
    onlineTotal: 0,
    lampOnlineTotal: 0,
    terminalOnlineTotal: 0,
    onlineRate: 0,
    lampOnlineRate: 0,
    terminalOnlineRate: 0,
    openAlarms: 0,
    todayAlarms: 0,
    criticalAlarms: 0,
    alarmsByStatus: [],
    alarmsByLevel: [],
    lampsByStatus: [],
    terminalsByStatus: [],
    deviceTypeStats: [],
    deviceBaseStats: { lamp: 0, terminal: 0, distributionBox: 0, camera: 0, sensor: 0, energyCollector: 0 },
    accessTrend: [],
    regionDeviceRanking: [],
    mapRegions: [],
    energy: null,
    recentAlarms: [],
    recentAlarmsTotal: 0,
    regionRanking: [],
  };
}

function normalizeSummary(data: Partial<DashboardSummary> | undefined): DashboardSummary {
  const fallback = createEmptySummary();
  if (!data) {
    return fallback;
  }

  return {
    ...fallback,
    ...data,
    assetTotal: Number(data.assetTotal ?? fallback.assetTotal),
    lampTotal: Number(data.lampTotal ?? fallback.lampTotal),
    terminalTotal: Number(data.terminalTotal ?? fallback.terminalTotal),
    onlineTotal: Number(data.onlineTotal ?? fallback.onlineTotal),
    lampOnlineTotal: Number(data.lampOnlineTotal ?? fallback.lampOnlineTotal),
    terminalOnlineTotal: Number(data.terminalOnlineTotal ?? fallback.terminalOnlineTotal),
    onlineRate: Number(data.onlineRate ?? fallback.onlineRate),
    lampOnlineRate: Number(data.lampOnlineRate ?? fallback.lampOnlineRate),
    terminalOnlineRate: Number(data.terminalOnlineRate ?? fallback.terminalOnlineRate),
    openAlarms: Number(data.openAlarms ?? fallback.openAlarms),
    todayAlarms: Number(data.todayAlarms ?? fallback.todayAlarms),
    criticalAlarms: Number(data.criticalAlarms ?? fallback.criticalAlarms),
    accessCityCount: Number(data.accessCityCount ?? fallback.accessCityCount),
    accessRegionCount: Number(data.accessRegionCount ?? fallback.accessRegionCount),
    metricChanges: {
      accessCityCount: Number(data.metricChanges?.accessCityCount ?? fallback.metricChanges.accessCityCount),
      accessRegionCount: Number(data.metricChanges?.accessRegionCount ?? fallback.metricChanges.accessRegionCount),
      assetTotal: Number(data.metricChanges?.assetTotal ?? fallback.metricChanges.assetTotal),
    },
    projectTotal: data.projectTotal ?? null,
    hasProjectTable: Boolean(data.hasProjectTable),
    hasEnergyTable: Boolean(data.hasEnergyTable),
    recentAlarms: data.recentAlarms ?? fallback.recentAlarms,
    regionRanking: data.regionRanking ?? fallback.regionRanking,
    alarmsByStatus: data.alarmsByStatus ?? fallback.alarmsByStatus,
    alarmsByLevel: data.alarmsByLevel ?? fallback.alarmsByLevel,
    lampsByStatus: data.lampsByStatus ?? fallback.lampsByStatus,
    terminalsByStatus: data.terminalsByStatus ?? fallback.terminalsByStatus,
    deviceTypeStats: data.deviceTypeStats ?? fallback.deviceTypeStats,
    deviceBaseStats: data.deviceBaseStats ?? fallback.deviceBaseStats,
    accessTrend: data.accessTrend ?? fallback.accessTrend,
    regionDeviceRanking: data.regionDeviceRanking ?? fallback.regionDeviceRanking,
    mapRegions: data.mapRegions ?? fallback.mapRegions,
    energy: data.energy ?? null,
  };
}

function formatNumber(value: number | string) {
  const numeric = Number(value);
  return Number.isFinite(numeric) ? numeric.toLocaleString('zh-CN') : String(value);
}

function formatSigned(value: number) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) {
    return '0';
  }
  return numeric > 0 ? `+${formatNumber(numeric)}` : formatNumber(numeric);
}

function formatPercent(value: number) {
  return `${Number(value).toFixed(2)}%`;
}

function formatTimeShort(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return value;
  }
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hour = String(date.getHours()).padStart(2, '0');
  const minute = String(date.getMinutes()).padStart(2, '0');
  return `${month}-${day} ${hour}:${minute}`;
}

function alarmStatusLabel(value: string) {
  const labels: Record<string, string> = {
    OPEN: '未处理',
    PROCESSING: '处理中',
    RESOLVED: '已恢复',
    IGNORED: '已忽略',
  };
  return labels[value] ?? value;
}

function projectLngLat(lng: number, lat: number) {
  const minLng = 73.5;
  const maxLng = 135.2;
  const minLat = 17.2;
  const maxLat = 53.8;
  return {
    x: 82 + ((lng - minLng) / (maxLng - minLng)) * 698,
    y: 489 - ((lat - minLat) / (maxLat - minLat)) * 432,
  };
}

function linePath(line: MapLine) {
  const from = line.from;
  const to = line.to;
  const midX = (from.x + to.x) / 2;
  const midY = (from.y + to.y) / 2 - Math.min(68, Math.abs(from.x - to.x) * 0.18 + 22);
  return `M ${from.x} ${from.y} Q ${midX} ${midY} ${to.x} ${to.y}`;
}

function chartTheme() {
  return {
    text: '#a9c7ec',
    grid: 'rgba(83, 168, 255, 0.15)',
    axis: '#5f83ac',
  };
}

function renderChart(key: string, el: HTMLDivElement | null, option: echarts.EChartsOption) {
  if (!el) {
    return;
  }
  const chart = charts.get(key) ?? echarts.init(el);
  chart.setOption(option, true);
  charts.set(key, chart);
}

function renderCharts() {
  const theme = chartTheme();

  renderChart('deviceType', deviceTypeChartRef.value, {
    color: deviceTypeStats.value.map((item) => item.color),
    tooltip: {
      trigger: 'item',
      backgroundColor: 'rgba(3, 20, 44, 0.94)',
      borderColor: 'rgba(60, 158, 255, 0.42)',
      textStyle: { color: '#e8f5ff' },
    },
    title: {
      text: '设备总数',
      subtext: formatNumber(summary.value.assetTotal),
      left: '31%',
      top: '41%',
      textAlign: 'center',
      textStyle: { color: '#9dbbe2', fontSize: 14, fontWeight: 500 },
      subtextStyle: { color: '#f7fbff', fontSize: 22, fontWeight: 800 },
    },
    series: [
      {
        type: 'pie',
        radius: ['48%', '72%'],
        center: ['32%', '52%'],
        avoidLabelOverlap: true,
        label: { show: false },
        labelLine: { show: false },
        itemStyle: { borderColor: '#05264f', borderWidth: 2 },
        data: deviceTypeStats.value.map((item) => ({ name: item.name, value: item.value })),
      },
    ],
  });

  renderChart('status', statusChartRef.value, {
    grid: { left: 46, right: 18, top: 20, bottom: 28 },
    tooltip: {
      trigger: 'axis',
      backgroundColor: 'rgba(3, 20, 44, 0.94)',
      borderColor: 'rgba(60, 158, 255, 0.42)',
      textStyle: { color: '#e8f5ff' },
    },
    xAxis: {
      type: 'category',
      data: runStatus.value.map((item) => item.label),
      axisLine: { lineStyle: { color: theme.axis } },
      axisTick: { show: false },
      axisLabel: { color: theme.text },
    },
    yAxis: {
      type: 'value',
      splitLine: { lineStyle: { color: theme.grid } },
      axisLabel: { color: theme.text },
    },
    series: [
      {
        type: 'bar',
        barWidth: 28,
        data: runStatus.value.map((item) => ({
          value: Math.max(item.value, 0),
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: item.color },
              { offset: 1, color: 'rgba(47, 140, 255, 0.45)' },
            ]),
          },
        })),
        label: {
          show: true,
          position: 'top',
          color: '#dff4ff',
          fontWeight: 700,
          formatter: (params: any) => formatNumber(params.value),
        },
      },
    ],
  });

  renderChart('trend', trendChartRef.value, {
    color: ['#21d7e9', '#6f9bff'],
    grid: { left: 40, right: 42, top: 28, bottom: 32 },
    tooltip: {
      trigger: 'axis',
      backgroundColor: 'rgba(3, 20, 44, 0.94)',
      borderColor: 'rgba(60, 158, 255, 0.42)',
      textStyle: { color: '#e8f5ff' },
    },
    legend: {
      top: 0,
      right: 0,
      textStyle: { color: theme.text },
      itemWidth: 10,
      itemHeight: 10,
    },
    xAxis: {
      type: 'category',
      data: trendMonths.value,
      axisLine: { lineStyle: { color: theme.axis } },
      axisTick: { show: false },
      axisLabel: { color: theme.text, fontSize: 10 },
    },
    yAxis: [
      {
        type: 'value',
        splitLine: { lineStyle: { color: theme.grid } },
        axisLabel: { color: theme.text },
      },
      {
        type: 'value',
        splitLine: { show: false },
        axisLabel: { color: theme.text },
      },
    ],
    series: [
      {
        name: '接入城市数（个）',
        type: 'line',
        smooth: true,
        symbolSize: 7,
        data: accessTrend.value,
        areaStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: 'rgba(33, 215, 233, 0.28)' },
            { offset: 1, color: 'rgba(33, 215, 233, 0)' },
          ]),
        },
      },
      {
        name: '接入设备数（台）',
        type: 'line',
        smooth: true,
        yAxisIndex: 1,
        symbolSize: 7,
        data: deviceTrend.value,
      },
    ],
  });

  renderChart('energy', energyChartRef.value, {
    color: ['#238cff', '#2bd8e8'],
    grid: { left: 48, right: 44, top: 30, bottom: 48, containLabel: true },
    tooltip: {
      trigger: 'axis',
      backgroundColor: 'rgba(3, 20, 44, 0.94)',
      borderColor: 'rgba(60, 158, 255, 0.42)',
      textStyle: { color: '#e8f5ff' },
    },
    legend: {
      top: 0,
      right: 16,
      textStyle: { color: theme.text },
      itemWidth: 12,
      itemHeight: 8,
    },
    xAxis: {
      type: 'category',
      data: energyDays,
      axisLine: { lineStyle: { color: theme.axis } },
      axisTick: { show: false },
      axisLabel: { color: theme.text, interval: 3, margin: 12 },
    },
    yAxis: [
      {
        type: 'value',
        name: '',
        splitLine: { lineStyle: { color: theme.grid } },
        axisLabel: { color: theme.text },
      },
      {
        type: 'value',
        min: 0,
        max: 50,
        axisLabel: { color: theme.text, formatter: '{value}%' },
        splitLine: { show: false },
      },
    ],
    series: [
      {
        name: '能耗（万kWh）',
        type: 'bar',
        barWidth: 10,
        data: energyUse,
        itemStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: '#31c9ff' },
            { offset: 1, color: '#136dff' },
          ]),
        },
      },
      {
        name: '节能率（%）',
        type: 'line',
        yAxisIndex: 1,
        smooth: true,
        symbolSize: 6,
        data: savingRate,
      },
    ],
  });
}

function resizeCharts() {
  charts.forEach((chart) => chart.resize());
  overviewMapInstance.value?.resize?.();
}

function overviewMarkerHtml(name: string, level: string) {
  return `<div class="overview-map-marker ${level === 'hot' ? 'is-hot' : ''}"><span></span><b>${name}</b></div>`;
}

function clearOverviewMapOverlays() {
  if (overviewMapInstance.value && overviewMapOverlays.value.length) {
    overviewMapInstance.value.remove(overviewMapOverlays.value);
  }
  overviewMapOverlays.value = [];
}

function renderOverviewMapOverlays(AMap: any) {
  if (!overviewMapInstance.value) {
    return;
  }
  clearOverviewMapOverlays();
  const overlays: any[] = [];
  const [hub, ...rest] = mapGeoCities.value;
  rest.forEach((city) => {
    overlays.push(
      new AMap.Polyline({
        path: [
          [hub.lng, hub.lat],
          [city.lng, city.lat],
        ],
        strokeColor: '#5edaff',
        strokeOpacity: 0.72,
        strokeWeight: 2,
        strokeStyle: 'dashed',
        zIndex: 22,
      }),
    );
  });

  mapGeoCities.value.forEach((city) => {
    overlays.push(
      new AMap.Marker({
        position: [city.lng, city.lat],
        content: overviewMarkerHtml(city.name, city.level),
        offset: new AMap.Pixel(-8, -8),
        zIndex: city.level === 'hot' ? 35 : 30,
      }),
    );
  });

  overviewMapInstance.value.add(overlays);
  overviewMapOverlays.value = overlays;
}

async function initOverviewMap() {
  if (!overviewMapRef.value || overviewMapReady.value) {
    return;
  }

  try {
    const AMap = await loadAmap();
    const mapConfig = getMapConfig();
    overviewMapInstance.value = new AMap.Map(overviewMapRef.value, {
      center: mapConfig.center,
      zoom: 4,
      zooms: [3, 12],
      mapStyle: 'amap://styles/darkblue',
      features: ['bg', 'road', 'building', 'point'],
      showLabel: true,
      viewMode: '2D',
    });
    overviewMapInstance.value.setBounds(new AMap.Bounds([73.5, 17.2], [135.2, 53.8]));
    renderOverviewMapOverlays(AMap);
    overviewMapReady.value = true;
  } catch {
    overviewMapReady.value = false;
  }
}

async function loadDashboard() {
  try {
    const data = await dashboardApi.summary({ recentPage: 1, recentPageSize: 7 });
    summary.value = normalizeSummary(data as Partial<DashboardSummary>);
    dashboardError.value = '';
  } catch {
    summary.value = createEmptySummary();
    dashboardError.value = '数据库统计数据加载失败';
  }
  await nextTick();
  renderCharts();
}

onMounted(async () => {
  await loadDashboard();
  await nextTick();
  void initOverviewMap();
  window.addEventListener('resize', resizeCharts);
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', resizeCharts);
  charts.forEach((chart) => chart.dispose());
  charts.clear();
  clearOverviewMapOverlays();
  overviewMapInstance.value?.destroy?.();
});
</script>

<style scoped>
.dashboard-screen {
  position: relative;
  min-width: 1180px;
  min-height: calc(100vh - 58px);
  padding: 10px 14px 14px;
  color: #d8e8fb;
  background:
    radial-gradient(circle at 50% 30%, rgba(5, 90, 180, 0.2), transparent 34%),
    linear-gradient(rgba(35, 128, 255, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(35, 128, 255, 0.06) 1px, transparent 1px);
  background-size: auto, 44px 44px, 44px 44px;
  overflow: hidden;
}

.summary-row {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
  height: 104px;
  margin-bottom: 10px;
}

.dashboard-error {
  position: absolute;
  top: 120px;
  left: 50%;
  z-index: 10;
  padding: 8px 14px;
  border: 1px solid rgba(255, 105, 77, 0.4);
  border-radius: 6px;
  color: #ffd9d2;
  background: rgba(93, 23, 27, 0.82);
  transform: translateX(-50%);
}

.summary-card,
.screen-panel {
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(35, 145, 255, 0.55);
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(8, 47, 98, 0.9), rgba(3, 24, 55, 0.88)),
    radial-gradient(circle at 18% 18%, rgba(40, 156, 255, 0.22), transparent 32%);
  box-shadow:
    inset 0 0 22px rgba(32, 132, 255, 0.16),
    inset 0 1px rgba(255, 255, 255, 0.08),
    0 12px 28px rgba(0, 0, 0, 0.24);
}

.summary-card::before,
.screen-panel::before {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(42, 170, 255, 0.28), transparent 20%, transparent 80%, rgba(42, 170, 255, 0.28)),
    linear-gradient(180deg, rgba(111, 198, 255, 0.18), transparent 34%);
  opacity: 0.7;
  content: "";
}

.summary-card {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 13px 18px;
}

.summary-icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(45, 151, 255, 0.34), rgba(11, 55, 116, 0.58));
  box-shadow: inset 0 0 18px rgba(67, 168, 255, 0.26), 0 0 18px rgba(20, 120, 255, 0.28);
}

.summary-icon span,
.summary-icon span::before,
.summary-icon span::after {
  position: absolute;
  display: block;
  content: "";
}

.summary-icon.city span {
  bottom: 14px;
  width: 10px;
  height: 24px;
  border: 2px solid #33c9ff;
  box-shadow: -14px 8px 0 -2px rgba(51, 201, 255, 0.75), 14px 4px 0 -1px rgba(51, 201, 255, 0.65);
}

.summary-icon.project span {
  width: 28px;
  height: 19px;
  border-radius: 4px;
  background: linear-gradient(180deg, #2cd8ff, #1d77ff);
}

.summary-icon.project span::before {
  top: -7px;
  left: 4px;
  width: 14px;
  height: 7px;
  border-radius: 4px 4px 0 0;
  background: #289fff;
}

.summary-icon.device span {
  width: 30px;
  height: 10px;
  border-radius: 2px;
  background: #37c8ff;
  box-shadow: 0 -10px 0 #218cff, 0 10px 0 #45e0ff;
  transform: skewY(-24deg);
}

.summary-icon.online span,
.summary-icon.offline span {
  width: 34px;
  height: 34px;
  border: 4px solid #31d6ff;
  border-radius: 50%;
}

.summary-icon.online span::before,
.summary-icon.offline span::before {
  top: 13px;
  left: 8px;
  width: 18px;
  height: 8px;
  border-bottom: 4px solid #31d6ff;
  border-left: 4px solid #31d6ff;
  transform: rotate(-45deg);
}

.summary-icon.offline span,
.summary-icon.offline span::before {
  border-color: #ff744f;
}

.summary-icon.alarm span {
  width: 34px;
  height: 28px;
  border-radius: 6px 6px 11px 11px;
  background: linear-gradient(180deg, #ff7c4d, #ff3d35);
}

.summary-icon.alarm span::before {
  top: -8px;
  left: 14px;
  width: 5px;
  height: 18px;
  border-radius: 999px;
  background: #ffd0bd;
}

.summary-content {
  position: relative;
  z-index: 1;
  min-width: 0;
}

.summary-content > span {
  display: block;
  color: #d9edff;
  font-size: 16px;
  font-weight: 700;
}

.summary-number {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 3px;
}

.summary-number strong {
  color: #eaf7ff;
  font-family: "DIN Alternate", "Roboto Mono", monospace;
  font-size: 30px;
  line-height: 1;
  text-shadow: 0 0 16px rgba(45, 158, 255, 0.42);
}

.summary-card.orange .summary-number strong,
.summary-card.red .summary-number strong {
  color: #ff5e4d;
}

.summary-number em {
  color: #c9d9ed;
  font-style: normal;
  font-weight: 700;
}

.summary-content p {
  margin: 6px 0 0;
  color: #aec8e6;
  font-size: 13px;
}

.summary-content b {
  margin-left: 7px;
  font-weight: 800;
}

.summary-content .up,
.energy-cards b {
  color: #31e7a0;
}

.summary-content .danger {
  color: #ff694d;
}

.summary-content .neutral {
  color: #d9ecff;
}

.command-grid {
  display: grid;
  grid-template-columns: 25.1% minmax(0, 43.6%) 31.3%;
  gap: 10px;
  height: calc(100vh - 182px);
  min-height: 720px;
}

.side-column,
.center-column {
  display: grid;
  gap: 10px;
  min-height: 0;
}

.left-column {
  grid-template-rows: 25.4% 23.2% 20.4% minmax(0, 1fr);
}

.center-column {
  grid-template-rows: minmax(0, 1fr) 32.5%;
}

.right-column {
  grid-template-rows: 32.2% 32.6% minmax(0, 1fr);
}

.screen-panel {
  min-height: 0;
  padding: 0;
}

.panel-header {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 32px;
  padding: 0 14px;
  border-bottom: 1px solid rgba(67, 154, 255, 0.24);
  background: linear-gradient(90deg, rgba(20, 90, 170, 0.52), rgba(5, 31, 68, 0.08));
}

.panel-header::before {
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 86px;
  height: 2px;
  background: linear-gradient(90deg, #23d6ff, transparent);
  content: "";
}

.panel-header h2 {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 0;
  color: #eef8ff;
  font-size: 16px;
  font-weight: 800;
}

.panel-header h2 span,
.panel-header small {
  color: #c4d9ef;
  font-size: 12px;
  font-weight: 700;
}

.panel-header button {
  border: 0;
  color: #aac7e8;
  background: transparent;
  cursor: pointer;
}

.chart {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
}

.donut-layout {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 44%;
  height: calc(100% - 32px);
  padding: 8px 10px 8px 6px;
}

.donut-chart {
  min-height: 190px;
}

.legend-list {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  min-width: 0;
  margin: 0;
  padding: 0 8px 0 0;
  list-style: none;
}

.legend-list li {
  display: grid;
  grid-template-columns: 9px minmax(0, 1fr) 54px;
  gap: 8px;
  align-items: center;
  color: #cde2f8;
  font-size: 13px;
}

.legend-list span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
}

.legend-list em {
  overflow: hidden;
  font-style: normal;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.legend-list strong {
  color: #e8f5ff;
  text-align: right;
}

.status-chart {
  height: calc(100% - 32px);
  min-height: 150px;
}

.trend-chart {
  height: calc(100% - 32px);
  min-height: 140px;
}

.base-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  height: calc(100% - 32px);
  padding: 14px;
}

.base-item {
  position: relative;
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr);
  grid-template-rows: 18px minmax(0, 1fr);
  align-items: center;
  min-height: 74px;
  padding: 12px;
  border: 1px solid rgba(51, 145, 255, 0.32);
  border-radius: 5px;
  background: rgba(7, 43, 91, 0.7);
}

.base-item i {
  position: relative;
  grid-row: 1 / 3;
  display: block;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: radial-gradient(circle at 50% 38%, rgba(48, 221, 255, 0.2), rgba(11, 70, 133, 0.5));
  box-shadow: 0 0 14px rgba(39, 207, 255, 0.45);
}

.base-item i::before,
.base-item i::after {
  position: absolute;
  content: "";
}

.base-item.pole i::before {
  right: 13px;
  bottom: 6px;
  width: 3px;
  height: 20px;
  border-radius: 999px;
  background: #36dbff;
  box-shadow: 0 0 8px rgba(54, 219, 255, 0.75);
}

.base-item.pole i::after {
  top: 7px;
  left: 8px;
  width: 18px;
  height: 9px;
  border: 2px solid #36dbff;
  border-bottom: 0;
  border-radius: 16px 16px 0 0;
  transform: rotate(-18deg);
}

.base-item.controller i::before {
  inset: 7px;
  border: 2px solid #36dbff;
  border-radius: 50%;
}

.base-item.controller i::after {
  top: 15px;
  left: 15px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #36dbff;
  box-shadow: 0 -10px 0 -1px #36dbff, 8px -5px 0 -1px #36dbff, 8px 5px 0 -1px #36dbff, 0 10px 0 -1px #36dbff, -8px 5px 0 -1px #36dbff, -8px -5px 0 -1px #36dbff;
}

.base-item.box i::before {
  inset: 6px 8px;
  border: 2px solid #36dbff;
  border-radius: 3px;
}

.base-item.box i::after {
  top: 12px;
  left: 14px;
  width: 6px;
  height: 10px;
  border-radius: 2px;
  background: #36dbff;
  box-shadow: 0 13px 0 -2px #36dbff;
}

.base-item.camera i::before {
  top: 8px;
  left: 7px;
  width: 20px;
  height: 14px;
  border: 2px solid #36dbff;
  border-radius: 10px;
}

.base-item.camera i::after {
  top: 13px;
  left: 14px;
  width: 6px;
  height: 6px;
  border: 2px solid #36dbff;
  border-radius: 50%;
  box-shadow: -4px 13px 0 -3px #36dbff, 4px 13px 0 -3px #36dbff;
}

.base-item.sensor i::before {
  top: 8px;
  left: 13px;
  width: 8px;
  height: 18px;
  border: 2px solid #36dbff;
  border-radius: 999px;
}

.base-item.sensor i::after {
  inset: 7px 4px;
  border-right: 2px solid #36dbff;
  border-left: 2px solid #36dbff;
  border-radius: 50%;
  box-shadow: inset 5px 0 0 -4px #36dbff, inset -5px 0 0 -4px #36dbff;
}

.base-item.energy i::before {
  top: 8px;
  left: 8px;
  width: 18px;
  height: 20px;
  border: 2px solid #36dbff;
  border-radius: 3px;
}

.base-item.energy i::after {
  top: 10px;
  left: 14px;
  width: 10px;
  height: 15px;
  clip-path: polygon(45% 0, 100% 0, 62% 42%, 100% 42%, 24% 100%, 45% 52%, 0 52%);
  background: #36dbff;
}

.base-item span {
  color: #b4cde8;
  font-size: 13px;
}

.base-item strong {
  color: #65dfff;
  font-family: "DIN Alternate", "Roboto Mono", monospace;
  font-size: 21px;
}

.map-panel {
  min-height: 460px;
}

.map-stage {
  position: relative;
  z-index: 1;
  height: calc(100% - 32px);
  min-height: 420px;
  overflow: hidden;
  background:
    radial-gradient(circle at 52% 48%, rgba(25, 139, 255, 0.18), transparent 32%),
    radial-gradient(circle at 52% 48%, rgba(25, 139, 255, 0.12) 0 1px, transparent 1.8px);
  background-size: auto, 18px 18px;
}

.overview-amap {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transition: opacity 0.24s ease;
}

.overview-amap.visible {
  opacity: 1;
}

.china-map {
  position: absolute;
  inset: 6px 4px 0;
  width: calc(100% - 8px);
  height: calc(100% - 6px);
  filter: drop-shadow(0 0 16px rgba(36, 139, 255, 0.56));
  overflow: visible;
}

.map-body {
  transform-box: fill-box;
  transform-origin: center;
}

.map-outline {
  fill: url(#mapFill);
  stroke: #128dff;
  stroke-width: 2.2;
  filter: url(#mapGlow);
}

.map-outline.hainan,
.map-outline.taiwan {
  fill: rgba(5, 42, 99, 0.92);
  stroke: #24a4ff;
  stroke-width: 1.8;
}

.province-line {
  fill: none;
  stroke: rgba(45, 151, 255, 0.34);
  stroke-width: 1.2;
}

.map-lines path {
  fill: none;
  stroke: rgba(98, 203, 255, 0.82);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 2 7;
  filter: drop-shadow(0 0 5px rgba(65, 202, 255, 0.82));
}

.map-city .pulse {
  fill: rgba(38, 167, 255, 0.14);
  stroke: rgba(81, 205, 255, 0.48);
  stroke-width: 2;
  animation: city-pulse 2.8s ease-out infinite;
}

.map-city .halo {
  fill: rgba(34, 157, 255, 0.52);
  stroke: #9ceaff;
  stroke-width: 2;
}

.map-city .dot {
  fill: #f7fbff;
  filter: drop-shadow(0 0 8px #55dcff);
}

.map-city.hot .halo {
  fill: rgba(255, 107, 65, 0.68);
  stroke: #ffca84;
}

.map-city.hot .dot {
  filter: drop-shadow(0 0 8px #ff6b41);
}

.map-city text {
  fill: #f8fbff;
  font-size: 17px;
  font-weight: 800;
  paint-order: stroke;
  stroke: rgba(0, 18, 45, 0.86);
  stroke-width: 4px;
}

:global(.overview-map-marker) {
  position: relative;
  display: flex;
  align-items: center;
  gap: 7px;
  color: #f8fbff;
  font-size: 14px;
  font-weight: 800;
  text-shadow: 0 2px 8px rgba(0, 12, 35, 0.95);
  white-space: nowrap;
}

:global(.overview-map-marker span) {
  position: relative;
  display: block;
  width: 14px;
  height: 14px;
  border: 2px solid #9ceaff;
  border-radius: 50%;
  background: rgba(34, 157, 255, 0.76);
  box-shadow: 0 0 12px rgba(85, 220, 255, 0.95);
}

:global(.overview-map-marker span::before) {
  position: absolute;
  inset: -9px;
  border: 1px solid rgba(81, 205, 255, 0.56);
  border-radius: 50%;
  content: "";
  animation: city-pulse 2.8s ease-out infinite;
}

:global(.overview-map-marker.is-hot span) {
  border-color: #ffca84;
  background: rgba(255, 107, 65, 0.86);
  box-shadow: 0 0 12px rgba(255, 107, 65, 0.95);
}

@keyframes city-pulse {
  0% {
    opacity: 0.8;
    transform: scale(0.72);
  }

  100% {
    opacity: 0;
    transform: scale(1.85);
  }
}

.map-legend {
  position: absolute;
  top: 16px;
  left: 18px;
  z-index: 2;
  display: grid;
  grid-template-columns: 12px 1fr;
  gap: 7px 8px;
  width: 150px;
  padding: 10px;
  border: 1px solid rgba(64, 151, 255, 0.28);
  border-radius: 5px;
  color: #bcd8f4;
  background: rgba(3, 22, 50, 0.78);
}

.map-legend span {
  grid-column: 1 / -1;
  font-size: 12px;
}

.map-legend i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.map-legend em {
  font-size: 12px;
  font-style: normal;
}

.map-legend .c5 {
  background: #ff744f;
}

.map-legend .c4 {
  background: #ffbd3f;
}

.map-legend .c3 {
  background: #35dfb5;
}

.map-legend .c2 {
  background: #2fd3ff;
}

.map-legend .c1 {
  background: #2f8cff;
}

.south-sea {
  position: absolute;
  right: 16px;
  bottom: 10px;
  z-index: 2;
  width: 70px;
  height: 70px;
  display: grid;
  place-items: end center;
  padding-bottom: 6px;
  border: 1px solid rgba(61, 157, 255, 0.5);
  color: #d5ecff;
  font-size: 12px;
  background:
    radial-gradient(circle at 22% 20%, #2ccaff 0 2px, transparent 3px),
    radial-gradient(circle at 65% 28%, #2ccaff 0 2px, transparent 3px),
    radial-gradient(circle at 42% 54%, #2ccaff 0 2px, transparent 3px),
    rgba(3, 22, 50, 0.62);
}

.overview-panel {
  min-height: 220px;
}

.overview-table,
.alarm-table {
  position: relative;
  z-index: 1;
  height: calc(100% - 32px);
  padding: 10px 12px 12px;
  overflow: hidden;
}

.table-head,
.table-row {
  display: grid;
  grid-template-columns: 1.4fr 0.82fr 0.82fr 0.72fr 0.82fr 0.82fr 0.82fr;
  gap: 8px;
  align-items: center;
}

.table-head,
.alarm-head {
  height: 28px;
  color: #9fc2e9;
  background: rgba(18, 67, 126, 0.42);
  font-size: 12px;
}

.table-head span,
.alarm-head span {
  overflow: hidden;
  padding: 0 6px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.table-row {
  height: 26px;
  border-bottom: 1px solid rgba(56, 143, 255, 0.12);
  color: #d3e7fb;
  font-size: 12px;
}

.table-row span,
.table-row time {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.table-row strong {
  color: #43e8a3;
}

.table-row em {
  color: #ff6e51;
  font-style: normal;
  font-weight: 800;
}

.table-row b {
  display: inline-flex;
  justify-content: center;
  width: 48px;
  border: 1px solid rgba(40, 221, 141, 0.36);
  border-radius: 4px;
  color: #24e192;
  background: rgba(17, 126, 82, 0.18);
  font-size: 12px;
}

.alarm-panel {
  min-height: 260px;
}

.alarm-head,
.alarm-row {
  display: grid;
  grid-template-columns: 0.64fr 1.35fr 1.04fr 0.9fr 0.9fr 0.64fr;
  gap: 8px;
  align-items: center;
}

.alarm-row {
  height: 28px;
  border-bottom: 1px solid rgba(56, 143, 255, 0.12);
  color: #d7e8fb;
  font-size: 12px;
}

.alarm-row strong {
  color: #ff694d;
}

.alarm-row:nth-child(3n) strong {
  color: #35d5ff;
}

.alarm-row span,
.alarm-row time {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.alarm-row b {
  color: #ff694d;
}

.alarm-row b.warning {
  color: #ffbd3f;
}

.alarm-row b.success {
  color: #2ce29a;
}

.rank-list {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: calc(100% - 32px);
  padding: 12px 18px 12px 16px;
}

.rank-row {
  display: grid;
  grid-template-columns: 24px 1.25fr minmax(110px, 1fr) 58px;
  gap: 10px;
  align-items: center;
  min-height: 20px;
  color: #dcecff;
  font-size: 13px;
}

.rank-row i {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  color: #c6d8ec;
  background: #425675;
  font-style: normal;
  font-weight: 800;
}

.rank-row i.top {
  color: #fff;
  background: linear-gradient(180deg, #ffc955, #ff8b18);
}

.rank-row span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rank-bar {
  height: 12px;
  overflow: hidden;
  background: rgba(34, 77, 132, 0.72);
}

.rank-bar em {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #2d82ff, #36e0f2);
  box-shadow: 0 0 12px rgba(54, 224, 242, 0.45);
}

.rank-row strong {
  color: #f4fbff;
  font-family: "DIN Alternate", "Roboto Mono", monospace;
  text-align: right;
}

.energy-panel {
  min-height: 260px;
}

.energy-cards {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding: 12px 14px 0;
}

.energy-cards div {
  min-width: 0;
  padding: 10px 8px;
  border: 1px solid rgba(53, 147, 255, 0.32);
  border-radius: 6px;
  text-align: center;
  background: rgba(8, 44, 92, 0.72);
}

.energy-cards span {
  display: block;
  color: #aac4e2;
  font-size: 13px;
}

.energy-cards strong {
  display: block;
  margin-top: 4px;
  color: #e7f5ff;
  font-family: "DIN Alternate", "Roboto Mono", monospace;
  font-size: 20px;
  line-height: 1.1;
}

.energy-cards strong em {
  margin-left: 4px;
  color: #bdcfea;
  font-size: 12px;
  font-style: normal;
}

.energy-cards b {
  display: block;
  margin-top: 5px;
  font-size: 12px;
}

.energy-empty {
  position: relative;
  z-index: 1;
  display: grid;
  place-content: center;
  gap: 10px;
  height: calc(100% - 32px);
  padding: 24px;
  color: #aac4e2;
  text-align: center;
}

.energy-empty strong {
  color: #e7f5ff;
  font-size: 20px;
}

.energy-empty span {
  max-width: 360px;
  line-height: 1.7;
}

.energy-chart {
  height: calc(100% - 104px);
  min-height: 168px;
  padding-bottom: 6px;
}

@media (max-width: 1400px) {
  .dashboard-screen {
    min-width: 1080px;
  }

  .summary-number strong {
    font-size: 26px;
  }

  .command-grid {
    grid-template-columns: 26% minmax(0, 42%) 32%;
  }
}
</style>
