<template>
  <div class="gis-dashboard">
    <div class="dashboard-header">
      <div class="header-title">
        <div class="title-main">
          <el-icon><MapLocation /></el-icon>
          <span>供水设施GIS一张图</span>
        </div>
        <div class="title-metrics">
          <span class="metric metric-danger">水源地 {{ summaryStats.sourceCount }}处</span>
          <span class="metric metric-primary">水厂 {{ summaryStats.plantCount }}座</span>
          <span class="metric metric-warning">泵站 {{ summaryStats.pumpStationCount }}座</span>
          <span class="metric">供水管网 {{ summaryStats.pipeCount }}条</span>
        </div>
      </div>
      <div class="header-actions">
        <el-input
          v-model="searchKeyword"
          class="search-input"
          placeholder="搜索设施名称/编号/地址"
          clearable
          @keyup.enter="handleSearch"
        >
          <template #prefix>
            <el-icon><Search /></el-icon>
          </template>
        </el-input>
        <el-button type="primary" @click="handleSearch">
          <el-icon><Search /></el-icon>
          搜索
        </el-button>
        <el-button @click="refreshScene">
          <el-icon><RefreshRight /></el-icon>
          刷新
        </el-button>
        <el-button @click="toggleFullscreen">
          <el-icon><FullScreen /></el-icon>
          全图
        </el-button>
      </div>
    </div>

    <div ref="screenRef" class="dashboard-content">
      <aside class="dashboard-sidebar">
        <el-card shadow="never" class="panel-card">
          <template #header>
            <span>图层控制</span>
          </template>
          <div class="layer-group">
            <div class="layer-title">供水设施图层</div>
            <el-checkbox v-for="item in facilityTypes" :key="item.value" v-model="layerState[item.value]">{{ item.label }}</el-checkbox>
          </div>
        </el-card>

        <el-card shadow="never" class="panel-card">
          <template #header>
            <span>图例说明</span>
          </template>
          <div class="legend-list">
            <div class="legend-item" v-for="item in facilityTypes" :key="item.value"><span class="legend-dot" :style="{ background: item.color }"></span><span>{{ item.label }}</span></div>
          </div>
        </el-card>

        <el-card shadow="never" class="panel-card stats-card">
          <template #header>
<span>供水设施统计</span>
          </template>
          <div class="stats-list">
            <div class="stats-row"><span>水源地</span><strong>{{ summaryStats.sourceCount }}</strong></div>
            <div class="stats-row"><span>水厂</span><strong>{{ summaryStats.plantCount }}</strong></div>
            <div class="stats-row"><span>泵站</span><strong>{{ summaryStats.pumpStationCount }}</strong></div>
            <div class="stats-row"><span>供水管网</span><strong>{{ summaryStats.pipeCount }}</strong></div>
            <div class="stats-row"><span>用水户</span><strong>{{ summaryStats.userCount }}</strong></div>
          </div>
        </el-card>

      </aside>

      <section class="dashboard-map-panel">
        <div class="map-toolbar">
          <div class="toolbar-left">
            <el-button @click="zoomIn">
              <el-icon><Plus /></el-icon>
              放大
            </el-button>
            <el-button @click="zoomOut">
              <el-icon><Minus /></el-icon>
              缩小
            </el-button>
            <el-button @click="locateCenter">
              <el-icon><Location /></el-icon>
              定位
            </el-button>
          </div>
          <div class="toolbar-right">当前坐标: {{ currentCoordinate }}</div>
        </div>
        <div id="gisMap" ref="mapRef" class="gis-map"></div>
        <div v-if="mapError" class="map-error">
          <el-result icon="warning" title="供水设施图层加载失败" :sub-title="mapError">
            <template #extra><el-button type="primary" @click="loadGisData">重试</el-button></template>
          </el-result>
        </div>
        <div v-else-if="mapEmpty" class="map-error">
          <el-empty description="当前筛选下没有可显示的供水设施" />
        </div>
      </section>
    </div>

    <el-drawer
      v-model="detailOpen"
      :title="detailTitle"
      direction="rtl"
      class="detail-drawer"
      size="560px"
      append-to-body
    >
      <el-skeleton v-if="detailLoading" :rows="8" animated />
      <el-result v-else-if="detailError" icon="warning" title="设施详情加载失败" :sub-title="detailError">
        <template #extra><el-button type="primary" @click="loadFacilityDetail">重试</el-button></template>
      </el-result>
      <el-descriptions v-else-if="detailRecord" :column="1" border>
        <el-descriptions-item v-for="item in detailEntries" :key="item.label" :label="item.label">
          {{ item.value }}
        </el-descriptions-item>
      </el-descriptions>
      <el-empty v-else description="暂无设施详情" />
    </el-drawer>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { FullScreen, Location, MapLocation, Minus, Plus, RefreshRight, Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { convertCoord } from '@/utils/coordTransform'
import { getWaterFacilityOverview, getWaterFacilityFeatures, getWaterFacilityDetail } from '@/api/pipeNetwork/waterSupply'
import { normalizeGeoJson, unwrapResponse } from '@/utils/waterSupplyModel'
import { getRequestErrorMessage } from '@/utils/waterFacilityQuery'
import { buildFacilityMapModel, filterFacilityMapModel, findFacilityFeature } from '@/utils/waterFacilityGisModel'

const centerPoint = { lng: 110.386, lat: 28.445 }

const MAP_BOUNDS = {
  southWest: { lng: 110.355, lat: 28.428 },
  northEast: { lng: 110.410, lat: 28.460 }
}
const MAP_MIN_ZOOM = 13
const MAP_MAX_ZOOM = 19
const MAP_DEFAULT_ZOOM = 15

const defaultLayerState = () => ({
  source: true,
  plant: true,
  pumpStation: true,
  pipe: true,
  user: true
})

const facilityTypes = [
  { label: '水源地', value: 'source', color: '#457b9d' },
  { label: '水厂', value: 'plant', color: '#2a9d8f' },
  { label: '泵站', value: 'pumpStation', color: '#e76f51' },
  { label: '管网', value: 'pipe', color: '#fcbf49' },
  { label: '用水户', value: 'user', color: '#7f4f24' }
]

const colorMap = {
  source: '#457b9d',
  plant: '#2a9d8f',
  pumpStation: '#e76f51',
  pipe: '#fcbf49',
  user: '#7f4f24'
}

const labelMap = {
  source: '水源地',
  plant: '水厂',
  pumpStation: '泵站',
  pipe: '供水管网',
  user: '用水户'
}

const mapRef = ref(null)
const screenRef = ref(null)
const searchKeyword = ref('')
const currentCoordinate = ref(`${centerPoint.lng}, ${centerPoint.lat}`)
const mapError = ref('')
const dataLoaded = ref(false)
const layerState = reactive(defaultLayerState())
const facilityOverview = ref([])
const pipelineData = ref([])
const pointData = ref([])
const detailOpen = ref(false)
const detailLoading = ref(false)
const detailError = ref('')
const detailRecord = ref(null)
const detailTarget = ref(null)

let mapInstance = null
let overlayClicked = false

const typeCount = type => {
  const row = facilityOverview.value.find(item => item.facilityType === type)
  return Number(row?.totalCount ?? row?.count ?? 0)
}

const summaryStats = computed(() => ({
  sourceCount: typeCount('source'),
  plantCount: typeCount('plant'),
  pumpStationCount: typeCount('pumpStation'),
  pipeCount: typeCount('pipe'),
  userCount: typeCount('user')
}))

const fullMapModel = computed(() => ({ points: pointData.value, lines: pipelineData.value }))
const visibleMapModel = computed(() => filterFacilityMapModel(fullMapModel.value, layerState))
const mapEmpty = computed(() => dataLoaded.value && !mapError.value && visibleMapModel.value.isEmpty)
const detailTitle = computed(() => `${labelMap[detailTarget.value?.type] || '供水设施'}详情`)
const detailEntries = computed(() => Object.entries(detailRecord.value || {})
  .filter(([, value]) => value !== null && value !== undefined && typeof value !== 'object')
  .map(([label, value]) => ({ label, value: value === '' ? '-' : value })))

watch(layerState, () => {
  renderMapScene()
}, { deep: true })

onMounted(async () => {
  await nextTick()
  await loadGisData()
  waitForBMapGL()
  document.addEventListener('fullscreenchange', handleFullscreenChange)
})

onBeforeUnmount(() => {
  document.removeEventListener('fullscreenchange', handleFullscreenChange)
  if (mapInstance) {
    mapInstance.clearOverlays()
    mapInstance = null
  }
})

function waitForBMapGL() {
  if (typeof BMapGL !== 'undefined') {
    initMap()
    return
  }

  let attempts = 0
  const maxAttempts = 50
  const timer = setInterval(() => {
    attempts++
    if (typeof BMapGL !== 'undefined') {
      clearInterval(timer)
      initMap()
      return
    }
    if (attempts >= maxAttempts) {
      clearInterval(timer)
      mapError.value = '百度地图脚本加载超时，请刷新页面重试。'
    }
  }, 200)
}

function initMap() {
  const el = document.getElementById('gisMap')
  if (!el) {
    mapError.value = '地图容器未找到。'
    return
  }

  try {
    mapInstance = new BMapGL.Map('gisMap', { enableMapClick: true })
  } catch (e) {
    console.error('创建地图实例失败：', e)
    mapError.value = '百度地图初始化失败，请检查AK和网络连接。'
    return
  }

  try {
    const [clng, clat] = convertCoord(centerPoint.lng, centerPoint.lat)
    const centerBMapPoint = new BMapGL.Point(clng, clat)
    mapInstance.centerAndZoom(centerBMapPoint, MAP_DEFAULT_ZOOM)
    mapInstance.enableScrollWheelZoom(true)
  } catch (e) {
    console.error('设置地图中心失败：', e)
  }

  try {
    if (typeof mapInstance.setMinZoom === 'function') {
      mapInstance.setMinZoom(MAP_MIN_ZOOM)
    }
    if (typeof mapInstance.setMaxZoom === 'function') {
      mapInstance.setMaxZoom(MAP_MAX_ZOOM)
    }
  } catch (e) {
    console.warn('设置缩放限制失败：', e)
  }

  try {
    if (typeof mapInstance.setMaxBounds === 'function') {
      const sw = new BMapGL.Point(MAP_BOUNDS.southWest.lng, MAP_BOUNDS.southWest.lat)
      const ne = new BMapGL.Point(MAP_BOUNDS.northEast.lng, MAP_BOUNDS.northEast.lat)
      mapInstance.setMaxBounds(new BMapGL.Bounds(sw, ne))
    }
  } catch (e) {
    console.warn('设置地图范围限制失败：', e)
  }

  mapInstance.addEventListener('click', () => {
    if (overlayClicked) {
      overlayClicked = false
      return
    }
    detailOpen.value = false
  })

  mapInstance.addEventListener('mousemove', (event) => {
    try {
      if (event && event.latlng) {
        currentCoordinate.value = `${event.latlng.lng.toFixed(4)}, ${event.latlng.lat.toFixed(4)}`
      }
    } catch (_) { /* ignore mousemove errors */ }
  })

  try {
    renderMapScene()
  } catch (e) {
    console.error('渲染地图要素失败：', e)
    mapError.value = '地图要素渲染失败。'
  }
}

function renderMapScene() {
  if (!mapInstance || typeof BMapGL === 'undefined') return

  try { mapInstance.clearOverlays() } catch (_) { /* ignore */ }
  try { drawPipelines() } catch (e) { console.warn('绘制供水管网失败：', e) }
  try { drawPoints() } catch (e) { console.warn('绘制供水设施失败：', e) }
}

function drawPipelines() {
  pipelineData.value.forEach(item => {
    try {
      if (!layerState[item.type]) return
      const pts = item.points.map(p => { const [lng, lat] = convertCoord(p.lng, p.lat); return new BMapGL.Point(lng, lat) })
      const polyline = new BMapGL.Polyline(pts, {
        strokeColor: colorMap[item.type],
        strokeWeight: 6,
        strokeOpacity: 0.92
      })
      try {
        polyline.addEventListener('click', (e) => {
          overlayClicked = true
          e.domEvent && e.domEvent.stopPropagation && e.domEvent.stopPropagation()
          openFacilityDetail(item)
        })
      } catch (_) { /* polyline click listener failed */ }
      mapInstance.addOverlay(polyline)

      const rawMid = getMidPoint(item.points)
      const mid = { lng: convertCoord(rawMid.lng, rawMid.lat)[0], lat: convertCoord(rawMid.lng, rawMid.lat)[1] }
      addPipeLabel(mid.lng, mid.lat, item.name, colorMap[item.type], item)
    } catch (e) {
      console.warn(`绘制供水管网 ${item.name} 失败：`, e)
    }
  })
}

function drawPoints() {
  pointData.value.forEach(item => {
    try {
      if (!layerState[item.type]) return
      const [plng, plat] = convertCoord(item.lng, item.lat)
      addPointMarker(plng, plat, colorMap[item.type], item.name, 'dot', () => {
        openFacilityDetail(item)
      })
    } catch (e) {
      console.warn(`绘制供水设施 ${item.name} 失败：`, e)
    }
  })
}

function addPipeLabel(lng, lat, text, color, rawItem) {
  const point = new BMapGL.Point(lng, lat)
  const html = `<div class="pipe-label" style="color:${color}">${text}</div>`
  const label = new BMapGL.Label(html, {
    position: point,
    offset: new BMapGL.Size(-28, -8)
  })
  label.setStyle({ border: 'none', background: 'transparent', padding: '0' })
  try {
    label.addEventListener('click', (e) => {
      overlayClicked = true
      e.domEvent && e.domEvent.stopPropagation && e.domEvent.stopPropagation()
      openFacilityDetail(rawItem)
    })
  } catch (_) { /* label click listener failed */ }
  mapInstance.addOverlay(label)
}

function addPointMarker(lng, lat, color, name, shape, onClick) {
  const point = new BMapGL.Point(lng, lat)
  const size = 22
  const borderRadius = shape === 'square' ? '3px' : '50%'
  const html = `<div class="marker-wrap" style="cursor:pointer">
    <div style="width:${size}px;height:${size}px;background:${color};border-radius:${borderRadius};border:3px solid #fff;box-shadow:0 2px 8px rgba(0,0,0,0.25);margin:0 auto;"></div>
    <div class="marker-text">${name}</div>
  </div>`
  const label = new BMapGL.Label(html, {
    position: point,
    offset: new BMapGL.Size(-size / 2 - 3, -size - 8)
  })
  label.setStyle({ border: 'none', background: 'transparent', padding: '0' })
  try {
    label.addEventListener('click', (e) => {
      overlayClicked = true
      e.domEvent && e.domEvent.stopPropagation && e.domEvent.stopPropagation()
      onClick && onClick()
    })
  } catch (_) { /* marker click listener failed */ }
  mapInstance.addOverlay(label)
}

function getMidPoint(points) {
  const first = points[0]
  const last = points[points.length - 1]
  return {
    lng: (first.lng + last.lng) / 2,
    lat: (first.lat + last.lat) / 2
  }
}

async function loadGisData() {
  mapError.value = ''
  dataLoaded.value = false
  try {
    const [overviewRes, featureRes] = await Promise.all([
      getWaterFacilityOverview(),
      getWaterFacilityFeatures()
    ])
    const overviewData = unwrapResponse(overviewRes)
    facilityOverview.value = Array.isArray(overviewData) ? overviewData : (overviewData?.facilityOverview || [])
    const geoJson = normalizeGeoJson(featureRes)
    const model = buildFacilityMapModel(geoJson)
    pipelineData.value = model.lines
    pointData.value = model.points
    dataLoaded.value = true
    if (mapInstance && typeof BMapGL !== 'undefined') renderMapScene()
  } catch (e) {
    console.error('加载供水GIS数据失败', e)
    facilityOverview.value = []
    pipelineData.value = []
    pointData.value = []
    dataLoaded.value = true
    mapError.value = getRequestErrorMessage(e, '供水GIS数据加载失败，请重试。')
  }
}

function handleSearch() {
  const keyword = searchKeyword.value.trim()
  if (!keyword) {
    ElMessage.warning('请输入搜索关键字')
    return
  }

  const matched = findFacilityFeature(visibleMapModel.value, keyword)

  if (!matched) {
    ElMessage.info('未找到匹配要素')
    return
  }

  const [mlng, mlat] = convertCoord(matched.lng, matched.lat)
  if (mapInstance && typeof BMapGL !== 'undefined') {
    mapInstance.centerAndZoom(new BMapGL.Point(mlng, mlat), 18)
  }

  openFacilityDetail(matched)

  ElMessage.success(`已定位到 ${matched.name}`)
}

async function openFacilityDetail(feature) {
  detailTarget.value = feature
  detailOpen.value = true
  detailRecord.value = null
  detailError.value = ''
  await loadFacilityDetail()
}

async function loadFacilityDetail() {
  const feature = detailTarget.value
  if (!feature?.type || feature.id === null || feature.id === undefined) return
  detailLoading.value = true
  detailError.value = ''
  try {
    detailRecord.value = unwrapResponse(await getWaterFacilityDetail(feature.type, feature.id))
  } catch (error) {
    detailRecord.value = null
    detailError.value = getRequestErrorMessage(error, '设施详情查询失败，请重试。')
  } finally {
    detailLoading.value = false
  }
}

async function refreshScene() {
  layerState.source = true
  layerState.plant = true
  layerState.pumpStation = true
  layerState.pipe = true
  layerState.user = true
  searchKeyword.value = ''
  detailOpen.value = false
  await loadGisData()
  locateCenter()
}

function locateCenter() {
  if (!mapInstance) return
  const [clng, clat] = convertCoord(centerPoint.lng, centerPoint.lat)
  mapInstance.centerAndZoom(new BMapGL.Point(clng, clat), MAP_DEFAULT_ZOOM)
}

function zoomIn() {
  if (!mapInstance) return
  const z = mapInstance.getZoom()
  if (z < MAP_MAX_ZOOM) mapInstance.zoomIn()
  else ElMessage.info('已放大至最大级别')
}

function zoomOut() {
  if (!mapInstance) return
  const z = mapInstance.getZoom()
  if (z > MAP_MIN_ZOOM) mapInstance.zoomOut()
  else ElMessage.info('已缩小至最小级别')
}

function toggleFullscreen() {
  const target = screenRef.value
  if (!target) return
  if (!document.fullscreenElement) {
    target.requestFullscreen?.()
    return
  }
  document.exitFullscreen?.()
}

function handleFullscreenChange() {
  if (!mapInstance) return
  const [clng, clat] = convertCoord(centerPoint.lng, centerPoint.lat)
  setTimeout(() => {
    mapInstance.centerAndZoom(new BMapGL.Point(clng, clat), mapInstance.getZoom())
  }, 200)
}
</script>


<style scoped>
.gis-dashboard {
  height: calc(100vh - 84px);
  padding: 16px;
  background: #f2f5f9;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.dashboard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 16px;
  margin-bottom: 14px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 4px 16px rgba(15, 35, 95, 0.05);
  flex-shrink: 0;
}

.header-title {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.title-main {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #1f2d3d;
  font-size: 17px;
  font-weight: 700;
}

.title-metrics {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.metric {
  position: relative;
  padding-left: 14px;
  color: #44546a;
  font-size: 13px;
}

.metric::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  transform: translateY(-50%);
}

.metric-danger::before { background: #e63946; }
.metric-primary::before { background: #457b9d; }
.metric-warning::before { background: #7f4f24; }

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

.search-input {
  width: 300px;
}

.dashboard-content {
  flex: 1;
  display: flex;
  gap: 14px;
  min-height: 0;
}

.dashboard-sidebar {
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex-shrink: 0;
  overflow-y: auto;
}

.panel-card {
  border: none;
  border-radius: 14px;
  box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05);
  flex-shrink: 0;
}

.layer-group {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid #eef2f8;
}

.layer-group-last {
  padding-bottom: 0;
  margin-bottom: 0;
  border-bottom: none;
}

.layer-title {
  color: #303133;
  font-size: 14px;
  font-weight: 600;
}

.legend-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #4e5969;
  font-size: 13px;
}

.legend-line {
  width: 34px;
  height: 0;
  border-top-width: 3px;
  border-top-style: solid;
  border-radius: 999px;
  flex-shrink: 0;
}

.legend-high { border-top-color: #e63946; }
.legend-sub-high { border-top-color: #f77f00; }
.legend-medium { border-top-color: #fcbf49; }
.legend-low { border-top-color: #2a9d8f; }

.legend-dot,
.legend-square {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

.legend-dot { border-radius: 50%; }
.legend-square { border-radius: 2px; }
.legend-valve { background: #457b9d; }
.legend-regulator { background: #e76f51; }
.legend-flowmeter { background: #2d6a4f; }
.legend-terminal { background: #6c757d; }
.legend-cover { background: #7f4f24; }
.legend-inspection { background: #b08968; }

.stats-card {
  background: linear-gradient(180deg, #f8fbff 0%, #f3f7fb 100%);
}

.stats-list {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

.stats-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #4e5969;
  font-size: 13px;
}

.stats-row strong {
  color: #1f2d3d;
  font-size: 14px;
}

.detail-card {
  flex: 1;
  min-height: 280px;
}

.dashboard-map-panel {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05);
  min-width: 0;
}

.map-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid #edf2f7;
  flex-shrink: 0;
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 8px;
}

.toolbar-right {
  color: #7a8699;
  font-size: 13px;
}

.gis-map {
  flex: 1;
  min-height: 0;
  background: #e9f0e5;
}

.map-error {
  position: absolute;
  inset: 52px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #909399;
  background: rgba(255, 255, 255, 0.85);
  font-size: 15px;
  z-index: 10;
}
</style>

<style>
.pipe-label {
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  text-shadow: 0 0 4px #fff, 0 0 4px #fff;
  background: rgba(255, 255, 255, 0.7);
  border-radius: 10px;
}

.marker-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.marker-text {
  margin-top: 3px;
  padding: 1px 7px;
  color: #2f3a4f;
  font-size: 11px;
  line-height: 1.3;
  white-space: nowrap;
  background: rgba(255, 255, 255, 0.78);
  border-radius: 10px;
  text-shadow: 0 0 3px #fff;
}

.BMap_cpyCtrl,
.anchorBL {
  display: none;
}
</style>
