<template>
  <div class="page">
    <section class="page-hero">
      <div>
        <h1>地图监控</h1>
        <p>在地图上查看单灯与监控终端点位、在线状态及报警；终端使用独立图标。</p>
      </div>
      <div class="map-actions">
        <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: 240px"
          @change="handleRegionChange"
        />
        <el-button type="primary" round @click="openFullscreen">打开全屏监控</el-button>
      </div>
    </section>
    <el-alert v-if="mapError" :title="mapError" type="warning" show-icon :closable="false" />
    <el-card class="modern-card" shadow="never">
      <template #header>
        <div class="toolbar">
          <span>地图监控</span>
          <div class="map-legend">
            <span><i class="lamp-legend-icon is-abnormal"></i>路灯异常</span>
            <span><i class="lamp-legend-icon is-on"></i>开灯</span>
            <span><i class="lamp-legend-icon is-off"></i>关灯</span>
            <span class="legend-gap"><i class="terminal-legend-icon is-ok"></i>终端正常</span>
            <span><i class="terminal-legend-icon is-bad"></i>终端异常</span>
          </div>
        </div>
      </template>
      <div ref="mapEl" class="map-container"></div>
    </el-card>
    <el-card class="modern-card" shadow="never">
      <template #header>点位列表</template>
      <el-table :data="listPoints" class="modern-table" @row-click="selectPoint">
        <el-table-column prop="name" label="名称" min-width="160" />
        <el-table-column prop="code" label="编号" min-width="140" />
        <el-table-column prop="deviceType" label="类型" width="120" />
        <el-table-column prop="regionName" label="区域" />
        <el-table-column label="在线状态">
          <template #default="{ row }">
            <el-tag :type="row.onlineStatus === 'ONLINE' ? 'success' : 'info'">{{ onlineStatusText(row.onlineStatus) }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="设备状态">
          <template #default="{ row }">
            <el-tag :type="row.activeAlarmCount > 0 ? 'danger' : row.status === 'NORMAL' ? 'success' : 'warning'">
              {{ lampStatusText(row.status) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="activeAlarmCount" label="未处理报警" width="130" />
      </el-table>
      <el-pagination
        class="pagination"
        layout="total, sizes, prev, pager, next"
        :total="total"
        :current-page="page"
        :page-size="pageSize"
        :page-sizes="[10, 20, 50, 100]"
        @current-change="handlePageChange"
        @size-change="handleSizeChange"
      />
    </el-card>
    <el-drawer v-model="detailVisible" :title="detailTitle" size="420px">
      <el-descriptions v-if="selectedPoint" :column="1" border>
        <el-descriptions-item label="名称">{{ selectedPoint.name }}</el-descriptions-item>
        <el-descriptions-item label="编号">{{ selectedPoint.code }}</el-descriptions-item>
        <el-descriptions-item label="所属区域">{{ selectedPoint.regionName }}</el-descriptions-item>
        <el-descriptions-item label="安装地址">{{ selectedPoint.address || '-' }}</el-descriptions-item>
        <el-descriptions-item label="设备类型">{{ selectedPoint.deviceType || '-' }}</el-descriptions-item>
        <el-descriptions-item label="在线状态">
          <el-tag :type="selectedPoint.onlineStatus === 'ONLINE' ? 'success' : 'info'">
            {{ onlineStatusText(selectedPoint.onlineStatus) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="设备状态">
          <el-tag :type="selectedPoint.status === 'NORMAL' ? 'success' : 'danger'">
            {{ lampStatusText(selectedPoint.status) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="坐标">{{ selectedPoint.longitude }}, {{ selectedPoint.latitude }}</el-descriptions-item>
        <el-descriptions-item label="最近上报">{{ formatTime(selectedPoint.lastReportedAt) }}</el-descriptions-item>
        <el-descriptions-item label="未处理报警">{{ selectedPoint.activeAlarmCount }}</el-descriptions-item>
      </el-descriptions>
      <el-divider>当前报警</el-divider>
      <el-timeline v-if="selectedPoint?.activeAlarms?.length">
        <el-timeline-item v-for="alarm in selectedPoint.activeAlarms" :key="alarm.id" :timestamp="formatTime(alarm.occurredAt)">
          <div class="alarm-title">{{ alarm.title }}</div>
          <div class="alarm-meta">{{ alarm.type }} / {{ alarmLevelText(alarm.level) }} / {{ alarmStatusText(alarm.status) }}</div>
        </el-timeline-item>
      </el-timeline>
      <el-empty v-else description="暂无未处理报警" />
    </el-drawer>
  </div>
</template>

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

const mapEl = ref<HTMLDivElement>();
const points = ref<any[]>([]);
const listPoints = ref<any[]>([]);
const regions = ref<any[]>([]);
const selectedRegionId = ref('');
const deviceTypeFilter = ref<'单灯' | '监控终端' | ''>('');
const page = ref(1);
const pageSize = ref(20);
const total = ref(0);
const mapError = ref('');
const mapConfig = getMapConfig();
const selectedPoint = ref<any>();
const detailVisible = ref(false);
const mapInstance = ref<any>();
const markerOverlays = ref<any[]>([]);
const normalMarkerZoom = 12;

const detailTitle = computed(() =>
  selectedPoint.value?.deviceType === '监控终端' ? '监控终端详情' : '路灯详情',
);

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

function openFullscreen() {
  const params = new URLSearchParams();
  if (selectedRegionId.value) {
    params.set('regionId', selectedRegionId.value);
  }
  if (deviceTypeFilter.value) {
    params.set('deviceType', deviceTypeFilter.value);
  }
  const query = params.toString() ? `?${params.toString()}` : '';
  window.open(`/map/fullscreen${query}`, '_blank');
}

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

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

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[];
  await loadList();
  try {
    const AMap = await loadAmap();
    renderMarkers(AMap);
  } catch (error) {
    mapError.value = error instanceof Error ? error.message : '地图加载失败';
  }
}

function handleRegionChange() {
  page.value = 1;
  void loadPoints();
}

function setDeviceTypeFilter(value: '单灯' | '监控终端' | '') {
  deviceTypeFilter.value = value;
  selectedPoint.value = undefined;
  detailVisible.value = false;
  page.value = 1;
  void loadPoints();
}

async function loadList() {
  const result = await mapApi.points({
    regionId: selectedRegionId.value,
    deviceType: deviceTypeFilter.value,
    page: page.value,
    pageSize: pageSize.value,
  });
  listPoints.value = result.items ?? [];
  total.value = result.total ?? 0;
}

function handlePageChange(value: number) {
  page.value = value;
  void loadList();
}

function handleSizeChange(value: number) {
  pageSize.value = value;
  page.value = 1;
  void loadList();
}

onMounted(async () => {
  regions.value = (await regionApi.tree()) as any[];
  points.value = (await mapApi.points({ deviceType: deviceTypeFilter.value, all: 1 })) as any[];
  await loadList();
  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,
    });
    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>
.map-container {
  height: 520px;
  border-radius: 8px;
  background: linear-gradient(135deg, #eff6ff, #e0f2fe);
}

.pagination {
  margin-top: 16px;
  justify-content: flex-end;
}

.map-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

.device-filter {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid rgba(37, 99, 235, 0.12);
  border-radius: 999px;
  background: rgba(248, 250, 252, 0.92);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);
}

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

.device-filter button:hover {
  color: #1d4ed8;
  background: rgba(37, 99, 235, 0.08);
}

.device-filter button.active {
  color: #fff;
  background: linear-gradient(135deg, #2563eb, #0ea5e9);
  box-shadow: 0 8px 18px rgba(37, 99, 235, 0.24);
}

.map-legend {
  display: flex;
  gap: 12px;
  align-items: center;
  color: #64748b;
  font-size: 13px;
}

.lamp-legend-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  margin-right: 5px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  vertical-align: middle;
}

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

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

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

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

.legend-gap {
  margin-left: 10px;
  padding-left: 12px;
  border-left: 1px solid #e2e8f0;
}

.terminal-legend-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  margin-right: 5px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  vertical-align: middle;
}

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

.terminal-legend-icon.is-bad {
  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.9);
  border-radius: 999px;
  color: #fff;
  background: linear-gradient(135deg, rgba(37, 99, 235, 0.92), rgba(14, 165, 233, 0.92));
  box-shadow: 0 10px 30px rgba(37, 99, 235, 0.35);
  cursor: pointer;
  font-size: 15px;
  font-weight: 800;
}

.alarm-title {
  font-weight: 600;
}

.alarm-meta {
  margin-top: 4px;
  color: #6b7280;
  font-size: 13px;
}
</style>
