<template>
  <div class="screen">
    <header class="screen-header">
      <div>
        <div class="eyebrow">城市照明综合网管平台</div>
        <h1>全国照明全屏监控</h1>
      </div>
      <div class="status">
        <div class="device-filter">
          <button :class="{ active: deviceTypeFilter === '' }" @click="setDeviceTypeFilter('')">全部</button>
          <button :class="{ active: deviceTypeFilter === '单灯' }" @click="setDeviceTypeFilter('单灯')">单灯</button>
          <button :class="{ active: deviceTypeFilter === '监控终端' }" @click="setDeviceTypeFilter('监控终端')">终端</button>
        </div>
        <el-tree-select
          v-model="selectedRegionId"
          :data="regions"
          :props="{ label: 'name', children: 'children' }"
          node-key="id"
          check-strictly
          clearable
          placeholder="按区域筛选"
          style="width: 220px"
          @change="loadPoints"
        />
        <span>点位 {{ points.length }}（单灯 {{ lampPointCount }} / 终端 {{ terminalPointCount }}）</span>
        <span>报警 {{ activeAlarmCount }}</span>
        <el-button type="primary" plain @click="closePage">退出全屏</el-button>
      </div>
    </header>

    <main class="screen-body">
      <section class="map-panel">
        <el-alert v-if="mapError" :title="mapError" type="warning" show-icon :closable="false" />
        <div ref="mapEl" class="fullscreen-map"></div>
      </section>
      <aside class="side-panel">
        <div class="panel-card">
          <span class="label">在线路灯</span>
          <strong>{{ onlineCount }}</strong>
        </div>
        <div class="panel-card danger">
          <span class="label">未处理报警</span>
          <strong>{{ activeAlarmCount }}</strong>
        </div>
        <div class="alarm-list">
          <div class="list-title">
            <h2>{{ selectedPoint ? '路灯状态详情' : '实时报警点位' }}</h2>
            <el-button v-if="selectedPoint" link type="primary" @click="selectedPoint = undefined">返回列表</el-button>
          </div>
          <div v-if="selectedPoint" class="lamp-detail">
            <div class="detail-name">{{ selectedPoint.name }}</div>
            <div class="detail-code">{{ selectedPoint.code }}</div>
            <dl>
              <div><dt>所属区域</dt><dd>{{ selectedPoint.regionName }}</dd></div>
              <div><dt>安装地址</dt><dd>{{ selectedPoint.address || '-' }}</dd></div>
              <div><dt>设备类型</dt><dd>{{ selectedPoint.deviceType || '-' }}</dd></div>
              <div><dt>在线状态</dt><dd>{{ onlineStatusText(selectedPoint.onlineStatus) }}</dd></div>
              <div><dt>设备状态</dt><dd>{{ lampStatusText(selectedPoint.status) }}</dd></div>
              <div><dt>最近上报</dt><dd>{{ formatTime(selectedPoint.lastReportedAt) }}</dd></div>
              <div><dt>坐标</dt><dd>{{ selectedPoint.longitude }}, {{ selectedPoint.latitude }}</dd></div>
            </dl>
            <div class="detail-alarms">
              <h3>当前报警 {{ selectedPoint.activeAlarmCount }}</h3>
              <div v-for="alarm in selectedPoint.activeAlarms" :key="alarm.id" class="alarm-item">
                <span>{{ alarm.title }}</span>
                <small>{{ alarm.type }} / {{ alarmLevelText(alarm.level) }} / {{ formatTime(alarm.occurredAt) }}</small>
              </div>
              <el-empty v-if="!selectedPoint.activeAlarms?.length" description="暂无未处理报警" />
            </div>
          </div>
          <template v-else>
            <div v-for="point in alarmPoints" :key="point.id" class="alarm-item" @click="selectPoint(point)">
              <span>{{ point.name }}</span>
              <small>{{ point.regionName }} / {{ alarmLevelText(point.highestAlarmLevel) }}</small>
            </div>
            <el-empty v-if="!alarmPoints.length" description="暂无未处理报警" />
          </template>
        </div>
      </aside>
    </main>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { mapApi, regionApi } from '../../api/modules';
import { BEIJING_CENTER, getBrowserCoordinates, getMapConfig, loadAmap } from '../../utils/amap';
import { alarmLevelText, lampStatusText, onlineStatusText } from '../../utils/labels';

const route = useRoute();
const mapEl = ref<HTMLDivElement>();
const points = ref<any[]>([]);
const regions = ref<any[]>([]);
const selectedRegionId = ref(typeof route.query.regionId === 'string' ? route.query.regionId : '');
const deviceTypeFilter = ref<'单灯' | '监控终端' | ''>(
  route.query.deviceType === '单灯' || route.query.deviceType === '监控终端' ? route.query.deviceType : '',
);
const mapError = ref('');
const selectedPoint = ref<any>();
const mapInstance = ref<any>();
const mapConfig = getMapConfig();
const markerOverlays = ref<any[]>([]);
const normalMarkerZoom = 12;
const onlineCount = computed(() => points.value.filter((point) => point.onlineStatus === 'ONLINE').length);
const terminalPointCount = computed(() => points.value.filter((p) => p.deviceType === '监控终端').length);
const lampPointCount = computed(() => points.value.length - terminalPointCount.value);
const alarmPoints = computed(() => points.value.filter((point) => point.activeAlarmCount > 0));
const activeAlarmCount = computed(() =>
  points.value.reduce((total, point) => total + Number(point.activeAlarmCount || 0), 0),
);

function closePage() {
  window.close();
}

function setDeviceTypeFilter(value: '单灯' | '监控终端' | '') {
  deviceTypeFilter.value = value;
  void loadPoints();
}

function formatTime(value?: string) {
  return value ? new Date(value).toLocaleString() : '-';
}

function selectPoint(point: any) {
  selectedPoint.value = point;
  mapInstance.value?.setZoomAndCenter(15, [point.longitude, point.latitude]);
}

function isMonitoringTerminal(point: any) {
  return point?.deviceType === '监控终端';
}

function lampState(point: any) {
  if (point.activeAlarmCount > 0 || ['FAULT', 'MAINTENANCE'].includes(point.status)) {
    return 'abnormal';
  }
  return point.isPowerOn === false || point.status === 'OFFLINE' || point.onlineStatus === 'OFFLINE' ? 'off' : 'on';
}

function terminalMarkerState(point: any) {
  if (
    point.activeAlarmCount > 0 ||
    ['FAULT', 'MAINTENANCE', 'OFFLINE'].includes(point.status) ||
    point.onlineStatus === 'OFFLINE'
  ) {
    return 'bad';
  }
  return 'ok';
}

function markerHtml(point: any) {
  if (isMonitoringTerminal(point)) {
    return `<div class="terminal-marker is-${terminalMarkerState(point)}"></div>`;
  }
  return `<div class="lamp-marker is-${lampState(point)}"></div>`;
}

function clusterGridSize(zoom: number) {
  if (zoom >= 10) {
    return 0.05;
  }
  if (zoom >= 8) {
    return 0.15;
  }
  if (zoom >= 6) {
    return 0.5;
  }
  return 1.5;
}

function clearMarkerOverlays() {
  if (mapInstance.value && markerOverlays.value.length) {
    mapInstance.value.remove(markerOverlays.value);
  }
  markerOverlays.value = [];
}

function groupedPoints(zoom: number, sourcePoints = points.value) {
  const gridSize = clusterGridSize(zoom);
  const groups = new Map<string, { count: number; longitude: number; latitude: number }>();
  sourcePoints.forEach((point) => {
    const lng = Number(point.longitude);
    const lat = Number(point.latitude);
    if (!Number.isFinite(lng) || !Number.isFinite(lat)) {
      return;
    }
    const key = `${Math.floor(lng / gridSize)}:${Math.floor(lat / gridSize)}`;
    const existing = groups.get(key);
    if (existing) {
      existing.count += 1;
      existing.longitude += lng;
      existing.latitude += lat;
      return;
    }
    groups.set(key, { count: 1, longitude: lng, latitude: lat });
  });

  return [...groups.values()].map((group) => ({
    count: group.count,
    longitude: group.longitude / group.count,
    latitude: group.latitude / group.count,
  }));
}

function clusterHtml(count: number) {
  return `<div class="map-cluster-marker">${count}</div>`;
}

function lngLatValue(value: any, key: 'lng' | 'lat') {
  const getter = key === 'lng' ? 'getLng' : 'getLat';
  return typeof value?.[getter] === 'function' ? Number(value[getter]()) : Number(value?.[key]);
}

function visiblePoints() {
  const bounds = mapInstance.value?.getBounds?.();
  const southWest = bounds?.getSouthWest?.();
  const northEast = bounds?.getNorthEast?.();
  if (!southWest || !northEast) {
    return points.value;
  }
  const minLng = lngLatValue(southWest, 'lng');
  const maxLng = lngLatValue(northEast, 'lng');
  const minLat = lngLatValue(southWest, 'lat');
  const maxLat = lngLatValue(northEast, 'lat');
  if (![minLng, maxLng, minLat, maxLat].every(Number.isFinite)) {
    return points.value;
  }

  return points.value.filter((point) => {
    const lng = Number(point.longitude);
    const lat = Number(point.latitude);
    return lng >= minLng && lng <= maxLng && lat >= minLat && lat <= maxLat;
  });
}

function renderMarkers(AMap: any) {
  if (!mapInstance.value) {
    return;
  }
  clearMarkerOverlays();
  const zoom = mapInstance.value.getZoom?.() ?? mapConfig.zoom;
  const visible = visiblePoints();
  if (zoom < normalMarkerZoom || visible.length > 1000) {
    markerOverlays.value = groupedPoints(zoom, visible).map((group) => {
      const marker = new AMap.Marker({
        position: [group.longitude, group.latitude],
        content: clusterHtml(group.count),
        offset: new AMap.Pixel(-24, -24),
      });
      marker.on('click', () => {
        mapInstance.value?.setZoomAndCenter(Math.min(Math.max(zoom + 2, normalMarkerZoom), 18), [group.longitude, group.latitude]);
      });
      marker.setMap(mapInstance.value);
      return marker;
    });
    return;
  }

  markerOverlays.value = visible.map((point) => {
    const marker = new AMap.Marker({
      position: [point.longitude, point.latitude],
      title: point.name,
      content: markerHtml(point),
    });
    marker.on('click', () => selectPoint(point));
    marker.setMap(mapInstance.value);
    return marker;
  });
}

async function loadPoints() {
  points.value = (await mapApi.points({
    regionId: selectedRegionId.value,
    deviceType: deviceTypeFilter.value,
    all: 1,
  })) as any[];
  selectedPoint.value = undefined;
  try {
    const AMap = await loadAmap();
    renderMarkers(AMap);
  } catch (error) {
    mapError.value = error instanceof Error ? error.message : '地图加载失败';
  }
}

onMounted(async () => {
  regions.value = (await regionApi.tree()) as any[];
  points.value = (await mapApi.points({
    regionId: selectedRegionId.value,
    deviceType: deviceTypeFilter.value,
    all: 1,
  })) as any[];
  try {
    const AMap = await loadAmap();
    let center = mapConfig.center;
    try {
      center = await getBrowserCoordinates();
    } catch {
      center = BEIJING_CENTER;
      mapError.value = '浏览器定位失败，已使用北京作为默认中心';
    }
    mapInstance.value = new AMap.Map(mapEl.value, {
      zoom: mapConfig.zoom,
      center,
      pitch: 42,
      viewMode: '3D',
      mapStyle: 'amap://styles/darkblue',
    });
    mapInstance.value.on('zoomend', () => renderMarkers(AMap));
    mapInstance.value.on('moveend', () => renderMarkers(AMap));
    renderMarkers(AMap);
  } catch (error) {
    mapError.value = error instanceof Error ? error.message : '地图加载失败';
  }
});
</script>

<style scoped>
.screen {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  height: 100vh;
  padding: 24px;
  overflow: hidden;
  color: #e0f2fe;
  background:
    radial-gradient(circle at top left, rgba(14, 165, 233, 0.28), transparent 34%),
    radial-gradient(circle at bottom right, rgba(99, 102, 241, 0.22), transparent 30%),
    #020617;
}

.screen-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.eyebrow {
  color: #38bdf8;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

h1,
h2 {
  margin: 0;
}

.screen-header h1 {
  margin-top: 8px;
  font-size: 30px;
}

.status {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 12px;
  align-items: center;
}

.status span {
  padding: 10px 14px;
  border: 1px solid rgba(125, 211, 252, 0.25);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.72);
}

.device-filter {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid rgba(56, 189, 248, 0.25);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.78);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.device-filter button {
  min-width: 54px;
  padding: 7px 13px;
  border: 0;
  border-radius: 999px;
  color: #bae6fd;
  background: transparent;
  cursor: pointer;
  font-weight: 700;
  transition: all 0.18s ease;
}

.device-filter button:hover {
  color: #fff;
  background: rgba(14, 165, 233, 0.18);
}

.device-filter button.active {
  color: #fff;
  background: linear-gradient(135deg, #0ea5e9, #7c3aed);
  box-shadow: 0 8px 22px rgba(14, 165, 233, 0.28);
}

.screen-body {
  display: grid;
  flex: 1 1 auto;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 18px;
  min-height: 0;
}

.map-panel,
.side-panel,
.panel-card,
.alarm-list {
  border: 1px solid rgba(125, 211, 252, 0.18);
  border-radius: 22px;
  background: rgba(15, 23, 42, 0.72);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(18px);
}

.map-panel {
  min-height: 0;
  overflow: hidden;
}

.fullscreen-map {
  width: 100%;
  height: 100%;
  min-height: 0;
}

.side-panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  padding: 14px;
}

.panel-card {
  padding: 20px;
}

.panel-card strong {
  display: block;
  margin-top: 10px;
  font-size: 38px;
  color: #67e8f9;
}

.panel-card.danger strong {
  color: #fb7185;
}

.label {
  color: #94a3b8;
}

.alarm-list {
  flex: 1;
  padding: 18px;
  overflow: auto;
}

.list-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}

.alarm-list h2 {
  margin: 0;
  font-size: 18px;
}

.alarm-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.14);
  cursor: pointer;
}

.alarm-item small {
  color: #94a3b8;
}

.lamp-detail {
  color: #e0f2fe;
}

.detail-name {
  font-size: 22px;
  font-weight: 700;
}

.detail-code {
  margin-top: 6px;
  color: #38bdf8;
}

dl {
  margin: 18px 0;
}

dl div {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.14);
}

dt {
  color: #94a3b8;
}

dd {
  margin: 0;
  word-break: break-all;
}

.detail-alarms h3 {
  margin: 0 0 8px;
  font-size: 16px;
}

:global(.lamp-marker) {
  width: 30px;
  height: 30px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  cursor: pointer;
  transform: translate(-15px, -30px);
}

:global(.lamp-marker.is-on) {
  background-image: url("../../assets/images/路灯正常新.png");
}

:global(.lamp-marker.is-off) {
  background-image: url("../../assets/images/路灯关新.png");
}

:global(.lamp-marker.is-abnormal) {
  background-image: url("../../assets/images/路灯异常新.png");
}

:global(.terminal-marker) {
  width: 32px;
  height: 32px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  cursor: pointer;
  transform: translate(-16px, -32px);
}

:global(.terminal-marker.is-ok) {
  background-image: url("../../assets/images/终端正常.png");
}

:global(.terminal-marker.is-bad) {
  background-image: url("../../assets/images/终端异常.png");
}

:global(.map-cluster-marker) {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 2px solid rgba(255, 255, 255, 0.88);
  border-radius: 999px;
  color: #fff;
  background: linear-gradient(135deg, rgba(14, 165, 233, 0.92), rgba(124, 58, 237, 0.92));
  box-shadow: 0 12px 34px rgba(14, 165, 233, 0.35);
  cursor: pointer;
  font-size: 15px;
  font-weight: 800;
}

@media (max-width: 1100px) {
  .screen-body {
    grid-template-columns: 1fr;
    height: auto;
  }
}
</style>
