<script setup lang="ts">
/**
 * OpenLayers地图组件
 * 支持多种底图切换：
 * - 高德地图（矢量）
 * - 高德卫星图
 * - 天地图（矢量）
 * - 天地图卫星图
 */
import type {
  InitParams,
  PhoneLocationStatus,
  PointData,
  PointLayerConfig,
  PolygonData,
  PolygonLayerConfig,
  TrackData,
  TrackGroupItem,
  WebGLPointOptions,
  WMSLayerConfig,
  WMSOptions,
} from './types'
import locationIcon from '@af-mobile-client-vue3/assets/img/component/positioning.png'
import startIcon from '@af-mobile-client-vue3/assets/img/map/start.png'
import terminusIcon from '@af-mobile-client-vue3/assets/img/map/terminus.png'
import { getConfigByName } from '@af-mobile-client-vue3/services/api/common'
import { mobileUtil } from '@af-mobile-client-vue3/utils/mobileUtil'
import { Map, View } from 'ol'
import { defaults as defaultControls, ScaleLine } from 'ol/control'
import Feature from 'ol/Feature'
import LineString from 'ol/geom/LineString'
import Point from 'ol/geom/Point'
import Polygon from 'ol/geom/Polygon'
import { defaults as defaultInteractions } from 'ol/interaction'
import { Image as ImageLayer, Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
import { fromLonLat, toLonLat } from 'ol/proj'
import { ImageWMS, Vector as VectorSource, XYZ } from 'ol/source'
import { Circle, Fill, Icon, Stroke, Style, Text } from 'ol/style'
import { Button } from 'vant'
import { getCurrentInstance, onUnmounted, ref } from 'vue'
import { wgs84ToGcj02Projection } from './utils/wgs84ToGcj02'
import 'vant/lib/index.css'

// 在 script setup 中添加
const emit = defineEmits<{
  (e: 'centerChange', center: [number, number]): void
}>()

// 获取当前组件实例
const instance = getCurrentInstance()

// 存储初始化参数
const mapParams = ref<InitParams>({})

/** 地图容器引用 */
const mapRef = ref<HTMLDivElement>()
/** 图层选择面板引用 */
const controlPanelsRef = ref<HTMLDivElement>()
/** 图层控制按钮引用 */
const controlToggleRef = ref<HTMLDivElement>()
/** 是否为预览模式 */
const preview = ref(false)
/** 地图实例 */
let map: Map | null = null
/** 当前底图类型 */
const currentMapType = ref<string>('gaode')
/** 控制图层面板显示状态 */
const showControls = ref<boolean>(false)

/** 图层选项配置 */
let layerOptions = [
  { text: '高德地图', value: 'gaode' },
  { text: '高德卫星', value: 'gaodeSatellite' },
  { text: '天地图', value: 'tianditu' },
  { text: '天地图卫星', value: 'tianditusatellite' },
]

/** 存储所有底图图层 */
const baseMaps: Record<string, TileLayer<XYZ>> = {}

/** 存储 WMS 图层 */
const wmsLayers: Record<string, ImageLayer<ImageWMS>> = {}

/** WMS 图层状态 */
const wmsLayerStatus = ref<WMSLayerConfig[]>([])

/** 存储点位图层 */
const vectorLayers: Record<number, VectorLayer<VectorSource>> = {}
const pointLayerStatus = ref<PointLayerConfig[]>([])

const tiandityKey = ref()
const gaodeKey = ref()
/** 导航模式 是否正在跟随定位 */
const isFollowingLocation = ref(false)
/** 定位定时器 */
let locationTimer: ReturnType<typeof setInterval> | null = null
/** 位置图标图层 */
let locationLayer: VectorLayer<VectorSource> | null = null

/** 存储轨迹图层 */
const trackLayers: Record<number, VectorLayer<VectorSource>> = {}
const trackLayerStatus = ref<TrackData[]>([])

/** 存储多边形图层 */
const polygonLayers: Record<number, VectorLayer<VectorSource>> = {}
const polygonLayerStatus = ref<PolygonLayerConfig[]>([])

/**
 * 创建位置图标图层
 */
function createLocationLayer(): VectorLayer<VectorSource> {
  const source = new VectorSource()
  return new VectorLayer({
    source,
    zIndex: 10, // 确保位置图标在最上层
  })
}

/**
 * 更新位置图标
 */
function updateLocationMarker(center: [number, number]): void {
  if (!map) {
    return
  }
  if (!locationLayer) {
    locationLayer = createLocationLayer()
    map?.addLayer(locationLayer)
  }
  const source = locationLayer.getSource()
  if (!source) {
    return
  }
  // 清除现有图标
  source.clear()

  // 创建新的位置图标要素
  const feature = new Feature({
    geometry: new Point(fromLonLat(center)),
  })

  // 设置图标样式
  const style = new Style({
    image: new Icon({
      src: locationIcon,
      scale: 0.2,
      anchor: [0.5, 0.5],
      anchorXUnits: 'fraction',
      anchorYUnits: 'fraction',
      crossOrigin: 'anonymous',
    }),
  })

  feature.setStyle(style)
  source.addFeature(feature)
}

/**
 * 切换地图图层
 * @param type - 图层类型
 */
function handleMapChange(type: string): void {
  // 隐藏所有图层
  Object.keys(baseMaps).forEach((key) => {
    baseMaps[key].setVisible(false)
  })

  // 根据选择显示对应图层
  switch (type) {
    case 'gaodeSatellite':
      baseMaps.gaodeSatellite.setVisible(true)
      baseMaps.gaodelabelLayer.setVisible(true)
      break
    case 'tianditu':
      baseMaps.tianditu.setVisible(true)
      baseMaps.tianditulabel.setVisible(true)
      break
    case 'tianditusatellite':
      baseMaps.tianditusatellite.setVisible(true)
      baseMaps.tianditusatlabel.setVisible(true)
      break
    default:
      baseMaps.gaode.setVisible(true)
  }
  currentMapType.value = type

  // 强制更新地图视图，确保切换后的图层显示正确
  if (map) {
    map.updateSize()
  }
}

/**
 * 初始化底图图层
 * @param tianDiTuKey - 天地图密钥
 */
function initializeLayers(tianDiTuKey = ''): void {
  try {
    // 高德地图
    baseMaps.gaode = new TileLayer({
      source: new XYZ({
        url: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
        crossOrigin: 'anonymous',
        projection: 'EPSG:3857',
        maxZoom: 18,
      }),
    })

    // 高德卫星图
    baseMaps.gaodeSatellite = new TileLayer({
      source: new XYZ({
        url: 'https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
        crossOrigin: 'anonymous',
        projection: 'EPSG:3857',
        maxZoom: 16,
      }),
      visible: false,
    })

    // 高德标注图层
    baseMaps.gaodelabelLayer = new TileLayer({
      source: new XYZ({
        url: 'https://webst02.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}',
        crossOrigin: 'anonymous',
        projection: 'EPSG:3857',
        maxZoom: 18,
      }),
      visible: false,
    })

    // 天地图矢量图层
    baseMaps.tianditu = new TileLayer({
      source: new XYZ({
        url: 'https://t0.tianditu.gov.cn/vec_w/wmts?'
          + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&'
          + `FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tianDiTuKey}`,
        projection: wgs84ToGcj02Projection,
        maxZoom: 18,
      }),
      visible: false,
    })

    // 天地图标注图层
    baseMaps.tianditulabel = new TileLayer({
      source: new XYZ({
        url: 'https://t0.tianditu.gov.cn/cva_w/wmts?'
          + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&'
          + `FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tianDiTuKey}`,
        projection: wgs84ToGcj02Projection,
        maxZoom: 18,
      }),
      visible: false,
    })

    // 天地图卫星图层
    baseMaps.tianditusatellite = new TileLayer({
      source: new XYZ({
        url: 'https://t0.tianditu.gov.cn/img_w/wmts?'
          + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&'
          + `FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tianDiTuKey}`,
        projection: wgs84ToGcj02Projection,
        maxZoom: 18,
      }),
      visible: false,
    })

    // 天地图卫星标注
    baseMaps.tianditusatlabel = new TileLayer({
      source: new XYZ({
        url: 'https://t0.tianditu.gov.cn/cia_w/wmts?'
          + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&'
          + `FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tianDiTuKey}`,
        projection: wgs84ToGcj02Projection,
        maxZoom: 18,
      }),
      visible: false,
    })
  }
  catch (error) {
    console.error('初始化地图图层失败:', error)
  }
}

/**
 * 获取地址信息
 * @param location - 经纬度坐标
 */
async function getAddressInfo(location: [number, number]): Promise<string> {
  try {
    const key = gaodeKey.value

    if (!key) {
      return '获取地址失败: 未配置密钥'
    }

    // 高德逆地址编码请求说明 https://amap.apifox.cn/api-14551463
    const response = await fetch(
      `https://restapi.amap.com/v3/geocode/regeo?location=${location[0].toFixed(6)},${location[1].toFixed(6)}&key=${key}&output=JSON`,
    )
    const data = await response.json()

    if (data.status === '1' && data.regeocode) {
      return data.regeocode.formatted_address
    }

    // 处理错误情况
    if (data.infocode === '10001') {
      return '获取地址失败: key 无效'
    }
    if (data.infocode === '10002') {
      return '获取地址失败: key 未配置平台'
    }
    return `获取地址失败: ${data.info || '未知错误'}`
  }
  catch (error) {
    return '获取地址失败: 网络错误'
  }
}

/**
 * 处理地图移动结束事件
 */
async function handleMoveEnd() {
  if (!map)
    return

  const view = map.getView()
  const center = view.getCenter()
  if (!center)
    return

  // 转换坐标为经纬度
  const lonLat = toLonLat(center)
  const formattedCenter: [number, number] = [Number(lonLat[0].toFixed(6)), Number(lonLat[1].toFixed(6))]

  // 直接发送事件，让父组件决定是否处理
  emit('centerChange', formattedCenter)
}

/**
 * 关闭图层选择组件
 */
function closeControlPanel(): void {
  if (showControls.value) {
    showControls.value = false
  }
}

/**
 * 初始化地图
 * @param params - 初始化参数
 */
function init(params: InitParams = {}): Promise<void> {
  return new Promise((resolve) => {
    if (!mapRef.value) {
      resolve()
      return
    }

    // 保存初始化参数
    mapParams.value = params

    // 设置默认参数
    const {
      center = [116.404, 39.915],
      zoom = 10,
      maxZoom = 21,
      minZoom = 4,
      isPreview = false,
    } = params
    // 设置预览模式
    preview.value = isPreview
    try {
      getConfigByName('mapConfig', (res) => {
        const tianDiTuKey = res.tianDiTuKey || 'c16876b28898637c0a1a68b3fa410504'
        const amapKey = res.amapKey || '5ebabc4536d4b42e0dd1e20175cca8ab'

        const initMapOptionConfig = res.mapOptionConfig
        if (initMapOptionConfig?.mapType) {
          currentMapType.value = initMapOptionConfig.mapType
        }
        if (initMapOptionConfig?.mapOption) {
          layerOptions = initMapOptionConfig.mapOption
        }

        tiandityKey.value = tianDiTuKey
        gaodeKey.value = amapKey
        // 初始化所有底图图层
        initializeLayers(tianDiTuKey)

        // 创建地图实例 - 加载所有底图图层，但默认只显示高德地图
        map = new Map({
          target: mapRef.value,
          layers: Object.values(baseMaps), // 加载所有底图图层
          view: new View({
            center: fromLonLat(center),
            zoom,
            projection: 'EPSG:3857',
            maxZoom,
            minZoom,
          }),
          controls: defaultControls({
            zoom: false,
            rotate: false,
            attribution: false,
          }).extend([
            new ScaleLine({
              units: 'metric',
              className: 'ol-scale-line',
            }),
          ]),
          interactions: defaultInteractions({
            altShiftDragRotate: false,
            pinchRotate: false,
          }),
        })

        // 更新地图大小，确保地图正确渲染
        setTimeout(() => {
          if (map) {
            map.updateSize()
            // 确保默认图层正确显示
            handleMapChange(currentMapType.value)
            // 地图初始化完成后解析 Promise
            resolve()
          }
        }, 200)

        // 监听地图移动结束事件
        map.on('moveend', handleMoveEnd)

        // 监听地图点击事件（使用 singleclick 避免与双击冲突）
        map.on('singleclick', closeControlPanel)

        // 设置鼠标样式
        if (mapRef.value) {
          mapRef.value.style.cursor = 'grab'
          // 监听地图事件
          const mapElement = mapRef.value

          // 鼠标按下时
          mapElement.addEventListener('mousedown', () => {
            mapElement.style.cursor = 'grabbing'
            // 用户开始拖动地图，取消跟随定位
            if (locationTimer) {
              isFollowingLocation.value = false
            }
          })

          // 触摸开始时
          mapElement.addEventListener('touchstart', () => {
            // 用户开始拖动地图，取消跟随定位
            if (locationTimer) {
              isFollowingLocation.value = false
            }
          })

          // 鼠标释放时
          mapElement.addEventListener('mouseup', () => {
            mapElement.style.cursor = 'grab'
          })

          // 鼠标离开地图时
          mapElement.addEventListener('mouseleave', () => {
            mapElement.style.cursor = 'grab'
          })
        }
      }, 'af-system')
    }
    catch (error) {
      console.error('地图初始化失败:', error)
      resolve()
    }
  })
}

/**
 * 获取地图实例
 * @returns OpenLayers Map 实例
 */
function getMap(): Map | null {
  return map
}

/**
 * 设置地图中心点
 * @param center - 经纬度坐标 [经度, 纬度]
 * @param animate - 是否使用动画效果，默认true
 */
function setCenter(center: [number, number], animate = true): void {
  if (!map)
    return

  const view = map.getView()
  if (animate) {
    view.animate({
      center: fromLonLat(center),
      duration: 500,
    })
  }
  else {
    view.setCenter(fromLonLat(center))
  }
}

/**
 * 设置地图缩放级别
 * @param zoom - 缩放级别
 * @param animate - 是否使用动画效果，默认true
 */
function setZoom(zoom: number, animate = true): void {
  if (!map)
    return

  const view = map.getView()
  if (animate) {
    view.animate({
      zoom,
      duration: 500,
    })
  }
  else {
    view.setZoom(zoom)
  }
}

/**
 * 获取当前地图缩放级别
 * @returns 当前缩放级别
 */
function getZoom(): number {
  if (!map)
    return 0
  return map.getView().getZoom() || 0
}

/**
 * 设置地图中心点和缩放级别
 * @param center - 经纬度坐标 [经度, 纬度]
 * @param zoom - 缩放级别
 * @param animate - 是否使用动画效果，默认true
 */
function setCenterAndZoom(center: [number, number], zoom: number, animate = true): void {
  if (!map)
    return

  const view = map.getView()
  if (animate) {
    view.animate({
      center: fromLonLat(center),
      zoom,
      duration: 500,
    })
  }
  else {
    view.setCenter(fromLonLat(center))
    view.setZoom(zoom)
  }
}

/**
 * 创建点位要素
 * @param point - 点位数据
 * @param icon - 图标URL
 * @param iconAnchor - 图标锚点
 * @returns 返回要素实例
 */
function createPointFeature(point: PointData, icon: string, iconAnchor: [number, number] = [0.5, 1], scale: number = 0.5): Feature {
  const feature = new Feature({
    geometry: new Point(fromLonLat([point.longitude, point.latitude])),
    properties: point,
  })

  const style = new Style({
    image: new Icon({
      src: icon,
      scale,
      anchor: iconAnchor,
      anchorXUnits: 'fraction',
      anchorYUnits: 'fraction',
      crossOrigin: 'anonymous',
    }),
    text: new Text({
      text: point.title || '',
      offsetY: -35,
      font: '12px sans-serif',
      fill: new Fill({
        color: '#333',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
  })

  feature.setStyle(style)
  return feature
}

/**
 * 创建多边形要素
 * @param polygon - 多边形数据
 * @returns 返回要素实例
 */
function createPolygonFeature(polygon: PolygonData): Feature {
  // 将经纬度坐标转换为地图坐标
  const coordinates = polygon.coordinates.map(coord => fromLonLat(coord))

  const feature = new Feature({
    geometry: new Polygon([coordinates]),
    properties: polygon,
  })

  // 设置多边形样式
  const style = new Style({
    fill: new Fill({
      color: polygon.fillColor || 'rgba(255, 0, 0, 0.3)',
    }),
    stroke: new Stroke({
      color: polygon.strokeColor || '#ff0000',
      width: polygon.strokeWidth || 2,
    }),
    text: new Text({
      text: polygon.name || '',
      font: '12px sans-serif',
      fill: new Fill({
        color: '#333',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
  })

  feature.setStyle(style)
  return feature
}

/**
 * 创建点位图层
 * @param config - 图层配置
 * @returns 返回图层实例
 */
function createPointLayer(config: PointLayerConfig): VectorLayer<VectorSource> {
  const vectorSource = new VectorSource()
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    visible: config.show,
    zIndex: config.id === undefined ? 1 : 3, // 根据是否有ID决定层级
  })

  // 添加点位要素
  const addFeatures = (data: PointData[]) => {
    // 清除现有要素
    vectorSource.clear()
    // 添加新的点位要素
    data.forEach((point) => {
      const feature = createPointFeature(point, config.icon, config.iconAnchor, config.scale)
      vectorSource.addFeature(feature)
    })
  }

  // 添加点击事件处理
  if (config.onClick) {
    map?.on('singleclick', (event) => {
      const feature = map.forEachFeatureAtPixel(event.pixel, feature => feature, {
        layerFilter: layer => layer === vectorLayer,
      })
      if (feature) {
        // 点击点位前，先关闭图层选择组件
        closeControlPanel()
        const properties = feature.getProperties()
        const { geometry, ...pointData } = properties
        config.onClick(pointData as PointData, event)
      }
    })
  }

  // 监听图层可见性变化
  vectorLayer.on('change:visible', async () => {
    if (vectorLayer.getVisible() && config.dataProvider) {
      try {
        const data = await config.dataProvider()
        addFeatures(data)
      }
      catch (error) {
        console.error('获取点位数据失败:', error)
      }
    }
  })

  // 初始加载数据
  if (config.show && config.dataProvider) {
    const result = config.dataProvider()
    if (result instanceof Promise) {
      result.then((data) => {
        addFeatures(data)
      }).catch((error) => {
        console.error('获取初始点位数据失败:', error)
      })
    }
    else {
      addFeatures(result)
    }
  }

  return vectorLayer
}

/**
 * 添加点位图层
 * @param config - 图层配置
 * @returns 返回图层实例
 */
function addPointLayer(config: PointLayerConfig): VectorLayer<VectorSource> | null {
  if (!map)
    return null
  if (vectorLayers[config.id]) {
    const oldVectorLayer = vectorLayers[config.id]
    oldVectorLayer.setVisible(false)
    oldVectorLayer.getSource().clear()
    map.removeLayer(oldVectorLayer)
    delete vectorLayers[config.id]
    // 强制刷新地图视图
    map.render()
  }
  const vectorLayer = createPointLayer(config)
  map.addLayer(vectorLayer)
  vectorLayers[config.id] = vectorLayer

  // 更新图层状态
  if (config.showInControl !== false) {
    const existingIndex = pointLayerStatus.value.findIndex(layer => layer.id === config.id)
    if (existingIndex === -1) {
      pointLayerStatus.value.push(config)
    }
    else {
      pointLayerStatus.value[existingIndex] = config
    }
  }

  return vectorLayer
}

/**
 * 添加海量点图层
 * @param config - 图层配置
 */
function addWebGLPoints(config: WebGLPointOptions): void {
  if (!map)
    return

  const vectorSource = new VectorSource()
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    visible: config.show,
    zIndex: config.id === undefined ? 1 : 3,
  })

  // 添加点位要素
  const addFeatures = (data: PointData[]) => {
    // 清除现有要素
    vectorSource.clear()
    // 添加新的点位要素
    data.forEach((point) => {
      const feature = createPointFeature(point, config.icon, config.iconAnchor)
      vectorSource.addFeature(feature)
    })
  }

  // 添加点击事件处理
  if (config.onClick) {
    map.on('singleclick', (event) => {
      const feature = map.forEachFeatureAtPixel(event.pixel, feature => feature, {
        layerFilter: layer => layer === vectorLayer,
      })
      if (feature) {
        // 点击点位前，先关闭图层选择组件
        closeControlPanel()
        const properties = feature.getProperties()
        const { geometry, ...pointData } = properties
        config.onClick(pointData as PointData, event)
      }
    })
  }

  // 初始加载数据
  if (config.show && config.dataProvider) {
    const result = config.dataProvider()
    if (result instanceof Promise) {
      result.then((data) => {
        addFeatures(data)
      }).catch((error) => {
        console.error('获取初始点位数据失败:', error)
      })
    }
    else {
      addFeatures(result)
    }
  }

  map.addLayer(vectorLayer)
  vectorLayers[config.id] = vectorLayer

  // 更新图层状态
  if (config.showInControl !== false) {
    const existingIndex = pointLayerStatus.value.findIndex(layer => layer.id === config.id)
    if (existingIndex === -1) {
      pointLayerStatus.value.push(config)
    }
    else {
      pointLayerStatus.value[existingIndex] = config
    }
  }
}

/**
 * 添加 WMS 图层
 * @param options - WMS 配置
 */
function addWMSLayers(options: WMSOptions): void {
  if (!map)
    return

  const { layers, wms } = options

  // 更新图层状态
  wmsLayerStatus.value = layers.map(layer => ({
    ...layer,
    show: layer.show,
  }))

  // 移除已存在的 WMS 图层
  Object.values(wmsLayers).forEach((layer) => {
    map?.removeLayer(layer)
  })

  // 清空图层记录
  Object.keys(wmsLayers).forEach((key) => {
    delete wmsLayers[key]
  })

  // 添加新的 WMS 图层
  layers.forEach((layerConfig) => {
    const wmsSource = new ImageWMS({
      url: wms.url,
      params: {
        LAYERS: layerConfig.layerName,
        FORMAT: wms.format,
        VERSION: wms.version,
        SRS: wms.srs,
      },
      ratio: 1,
      serverType: 'geoserver',
    })

    const wmsLayer = new ImageLayer({
      source: wmsSource,
      visible: layerConfig.show,
      zIndex: 2,
    })

    wmsLayers[layerConfig.layerName] = wmsLayer
    map.addLayer(wmsLayer)
  })
}

/**
 * 控制 WMS 图层显示/隐藏
 * @param layerName - 图层名称
 * @param visible - 是否显示
 */
function setWMSLayerVisible(layerName: string, visible: boolean): void {
  const layer = wmsLayers[layerName]
  if (layer) {
    layer.setVisible(visible)
  }
}

/**
 * 切换 WMS 图层显示状态
 * @param layer - 图层配置
 */
function handleToggleWMSLayer(layer: WMSLayerConfig): void {
  layer.show = !layer.show
  setWMSLayerVisible(layer.layerName, layer.show)
}

/**
 * 控制点位图层显示/隐藏
 * @param layerId - 图层ID
 * @param visible - 是否显示
 */
function setPointLayerVisible(layerId: number, visible: boolean): void {
  const layer = vectorLayers[layerId]
  if (layer) {
    layer.setVisible(visible)
    // 更新图层状态
    const layerIndex = pointLayerStatus.value.findIndex(layer => layer.id === layerId)
    if (layerIndex !== -1) {
      pointLayerStatus.value[layerIndex].show = visible
    }
  }
}

/**
 * 切换点位图层显示状态
 */
function handleTogglePointLayer(layer: PointLayerConfig): void {
  layer.show = !layer.show
  setPointLayerVisible(layer.id, layer.show)
}

/** 处理定位请求 */
async function handleLocation() {
  if (!map)
    return

  // 如果是导航模式，重新开启跟随定位
  if (locationTimer) {
    isFollowingLocation.value = true
  }

  try {
    mobileUtil.execute({
      param: {},
      funcName: 'getLocationResult',
      callbackFunc: (result) => {
        const res = result as PhoneLocationStatus
        if (res.status === 'success') {
          const locationResult = JSON.parse(res.data.location)
          if (locationResult.longitude && locationResult.latitude) {
            const center: [number, number] = [locationResult.longitude, locationResult.latitude]
            setCenterAndZoom(center, getZoom())
            // 更新位置图标
            if (isFollowingLocation.value) {
              updateLocationMarker(center)
            }
          }
        }
      },
    })
  }
  catch (error) {
    // 在 web 端测试时，使用模拟数据
    console.log('获取实时位置失败，使用模拟数据')
    // 生成一个随机偏移量
    const offset = (Math.random() - 0.5) * 0.01
    const center: [number, number] = [
      108.948024 + offset, // 西安经度
      34.263161 + offset, // 西安纬度
    ]

    setCenterAndZoom(center, getZoom())
    // 更新位置图标
    if (isFollowingLocation.value) {
      updateLocationMarker(center)
    }
  }
}

// 开启导航
function startNavigation() {
  isFollowingLocation.value = true

  // 创建并添加位置图标图层
  if (!locationLayer) {
    locationLayer = createLocationLayer()
    map?.addLayer(locationLayer)
  }

  locationTimer = setInterval(() => {
    navigationHandleLocation()
  }, 1000)
}

function stopNavigation() {
  isFollowingLocation.value = false
  if (locationTimer) {
    clearInterval(locationTimer)
    locationTimer = null
  }

  // 移除位置图标图层
  if (locationLayer && map) {
    map.removeLayer(locationLayer)
    locationLayer = null
  }
}

function navigationHandleLocation() {
  // 打开导航定时器, 并且跟随定位
  if (isFollowingLocation.value) {
    handleLocation()
  }
}

/**
 * 创建多边形图层
 * @param config - 图层配置
 * @returns 返回图层实例
 */
function createPolygonLayer(config: PolygonLayerConfig): VectorLayer<VectorSource> {
  const vectorSource = new VectorSource()
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    visible: config.show,
    zIndex: config.id === undefined ? 1 : 4, // 多边形图层层级稍高
  })

  // 添加多边形要素
  const addFeatures = (data: PolygonData[]) => {
    // 清除现有要素
    vectorSource.clear()
    // 添加新的多边形要素
    data.forEach((polygon) => {
      const feature = createPolygonFeature(polygon)
      vectorSource.addFeature(feature)
    })
  }

  // 添加点击事件处理
  if (config.onClick) {
    map?.on('singleclick', (event) => {
      const feature = map.forEachFeatureAtPixel(event.pixel, feature => feature, {
        layerFilter: layer => layer === vectorLayer,
      })
      if (feature) {
        // 点击多边形前，先关闭图层选择组件
        closeControlPanel()
        const properties = feature.getProperties()
        const { geometry, ...polygonData } = properties
        config.onClick(polygonData as PolygonData, event)
      }
    })
  }

  // 监听图层可见性变化
  vectorLayer.on('change:visible', async () => {
    if (vectorLayer.getVisible() && config.dataProvider) {
      try {
        const data = await config.dataProvider()
        addFeatures(data)
      }
      catch (error) {
        console.error('获取多边形数据失败:', error)
      }
    }
  })

  // 初始加载数据
  if (config.show && config.dataProvider) {
    const result = config.dataProvider()
    if (result instanceof Promise) {
      result.then((data) => {
        addFeatures(data)
      }).catch((error) => {
        console.error('获取初始多边形数据失败:', error)
      })
    }
    else {
      addFeatures(result)
    }
  }

  return vectorLayer
}

/**
 * 添加多边形图层
 * @param config - 图层配置
 * @returns 返回图层实例
 */
function addPolygonLayer(config: PolygonLayerConfig): VectorLayer<VectorSource> | null {
  if (!map)
    return null
  // 如果该ID的图层已存在，先移除旧的
  if (polygonLayers[config.id]) {
    const oldVectorLayer = polygonLayers[config.id]
    oldVectorLayer.setVisible(false)
    oldVectorLayer.getSource().clear()
    map.removeLayer(oldVectorLayer)
    delete polygonLayers[config.id]
    // 强制刷新地图视图
    map.render()
  }
  const vectorLayer = createPolygonLayer(config)
  map.addLayer(vectorLayer)
  polygonLayers[config.id] = vectorLayer

  // 更新图层状态
  if (config.showInControl !== false) {
    const existingIndex = polygonLayerStatus.value.findIndex(layer => layer.id === config.id)
    if (existingIndex === -1) {
      polygonLayerStatus.value.push(config)
    }
    else {
      polygonLayerStatus.value[existingIndex] = config
    }
  }

  return vectorLayer
}

/**
 * 添加轨迹图层
 * @param trackData - 轨迹数据
 */
function addTrackLayer(trackData: TrackData): void {
  if (!map)
    return

  const vectorSource = new VectorSource()
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    visible: true,
    zIndex: 2,
  })

  // 创建轨迹线要素
  const coordinates = trackData.trackData.map(coord => fromLonLat(coord))
  const lineString = new Feature({
    geometry: new LineString(coordinates),
  })

  // 设置轨迹线样式
  const lineStyle = new Style({
    stroke: new Stroke({
      color: trackData.color,
      width: 3,
    }),
  })
  lineString.setStyle(lineStyle)

  // 创建起点和终点图标
  const startPoint = new Feature({
    geometry: new Point(coordinates[0]),
  })
  const endPoint = new Feature({
    geometry: new Point(coordinates[coordinates.length - 1]),
  })

  // 设置起点图标样式 - 使用绿色圆形图标
  const startStyle = new Style({
    image: new Circle({
      radius: 8,
      fill: new Fill({
        color: '#4CAF50',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
    text: new Text({
      text: '起点',
      offsetY: -15,
      font: '12px sans-serif',
      fill: new Fill({
        color: '#333',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
  })

  // 设置终点图标样式 - 使用红色圆形图标
  const endStyle = new Style({
    image: new Circle({
      radius: 8,
      fill: new Fill({
        color: '#F44336',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
    text: new Text({
      text: '终点',
      offsetY: -15,
      font: '12px sans-serif',
      fill: new Fill({
        color: '#333',
      }),
      stroke: new Stroke({
        color: '#fff',
        width: 2,
      }),
    }),
  })

  startPoint.setStyle(startStyle)
  endPoint.setStyle(endStyle)

  // 添加要素到图层
  vectorSource.addFeatures([lineString, startPoint, endPoint])

  // 添加到地图
  map.addLayer(vectorLayer)
  trackLayers[trackData.id] = vectorLayer
  // 更新图层状态，确保 show 属性被正确设置
  const trackDataWithShow = {
    ...trackData,
    show: true, // 默认显示
  }
  trackLayerStatus.value.push(trackDataWithShow)
}

/**
 * 添加分段轨迹组图层
 * @param trackGroups - 轨迹组数组，每组包含唯一 key 和 path
 * @param options - 轨迹图层配置
 * @param options.id - 图层ID
 * @param options.name - 图层名称
 * @param options.color - 统一颜色，控制所有轨迹段
 * @param options.show - 图层是否默认显示
 */
function addTrackGroupLayer(
  trackGroups: TrackGroupItem[],
  options: {
    id?: number
    name?: string
    color?: string
    show?: boolean
  } = {},
): void {
  if (!map || !Array.isArray(trackGroups) || trackGroups.length === 0)
    return

  const layerId = options.id ?? 0
  const layerName = options.name ?? (trackGroups.length === 1 ? trackGroups[0].key : '轨迹组')
  const layerColor = options.color || '#ff0000'
  const visible = options.show ?? true

  const vectorSource = new VectorSource()
  const vectorLayer = new VectorLayer({
    source: vectorSource,
    visible,
    zIndex: 2,
  })

  trackGroups.forEach((group) => {
    if (!group.path || group.path.length === 0)
      return

    const coordinates = group.path.map(coord => fromLonLat([coord.x, coord.y]))
    if (coordinates.length > 1) {
      const lineString = new Feature({
        geometry: new LineString(coordinates),
      })

      const lineStyle = new Style({
        stroke: new Stroke({
          color: layerColor,
          width: 4,
        }),
      })

      lineString.setStyle(lineStyle)
      vectorSource.addFeature(lineString)
    }

    const startPoint = new Feature({
      geometry: new Point(coordinates[0]),
      properties: { segmentKey: group.key, type: 'start' },
    })
    const endPoint = new Feature({
      geometry: new Point(coordinates[coordinates.length - 1]),
      properties: { segmentKey: group.key, type: 'end' },
    })

    const pointStyle = (src: string, text: string) =>
      new Style({
        image: new Icon({
          src,
          anchor: [0.5, 1],
          scale: 0.15,
          crossOrigin: 'anonymous',
        }),
        text: new Text({
          text,
          offsetY: 10,
          font: '12px sans-serif',
          fill: new Fill({
            color: '#333',
          }),
          stroke: new Stroke({
            color: '#fff',
            width: 2,
          }),
        }),
      })

    // 解析时间：key格式为 "开始时间-结束时间"
    const [startTime, endTime] = group.key.split('-')

    startPoint.setStyle(pointStyle(startIcon, startTime || '起点'))
    endPoint.setStyle(pointStyle(terminusIcon, endTime || '终点'))

    vectorSource.addFeatures([startPoint, endPoint])
  })

  map.addLayer(vectorLayer)
  trackLayers[layerId] = vectorLayer

  const flatTrackData = trackGroups.reduce<[number, number][]>((acc, group) => {
    if (group.path && group.path.length > 0) {
      acc.push(...group.path.map(p => [p.x, p.y] as [number, number]))
    }
    return acc
  }, [])

  trackLayerStatus.value.push({
    id: layerId,
    name: layerName,
    trackData: flatTrackData,
    color: layerColor,
    show: visible,
  })
}

/**
 * 控制轨迹图层显示/隐藏
 * @param trackId - 轨迹ID
 * @param visible - 是否显示
 */
function setTrackLayerVisible(trackId: number, visible: boolean): void {
  const layer = trackLayers[trackId]
  if (layer) {
    layer.setVisible(visible)
    // 更新图层状态
    const layerIndex = trackLayerStatus.value.findIndex(layer => layer.id === trackId)
    if (layerIndex !== -1) {
      trackLayerStatus.value[layerIndex].show = visible
    }
  }
}

/**
 * 切换轨迹图层显示状态
 */
function handleToggleTrackLayer(track: TrackData): void {
  track.show = !track.show
  setTrackLayerVisible(track.id, track.show)
}

/**
 * 控制多边形图层显示/隐藏
 * @param layerId - 图层ID
 * @param visible - 是否显示
 */
function setPolygonLayerVisible(layerId: number, visible: boolean): void {
  const layer = polygonLayers[layerId]
  if (layer) {
    layer.setVisible(visible)
    // 更新图层状态
    const layerIndex = polygonLayerStatus.value.findIndex(layer => layer.id === layerId)
    if (layerIndex !== -1) {
      polygonLayerStatus.value[layerIndex].show = visible
    }
  }
}

/**
 * 切换多边形图层显示状态
 */
function handleTogglePolygonLayer(layer: PolygonLayerConfig): void {
  layer.show = !layer.show
  setPolygonLayerVisible(layer.id, layer.show)
}

// 暴露方法给父组件
defineExpose({
  updateLocationMarker,
  init,
  getMap,
  setCenter,
  setZoom,
  getZoom,
  setCenterAndZoom,
  addPointLayer,
  addWebGLPoints,
  addWMSLayers,
  setWMSLayerVisible,
  handleToggleWMSLayer,
  setPointLayerVisible,
  handleTogglePointLayer,
  getAddressInfo,
  handleLocation,
  startNavigation,
  stopNavigation,
  addTrackLayer,
  addTrackGroupLayer,
  setTrackLayerVisible,
  handleToggleTrackLayer,
  addPolygonLayer,
  setPolygonLayerVisible,
  handleTogglePolygonLayer,
})

// 组件卸载时清理地图实例
onUnmounted(() => {
  if (map) {
    stopNavigation()
    map.setTarget(undefined)
    map = null
  }
  if (locationTimer) {
    clearInterval(locationTimer)
    locationTimer = null
  }
  locationLayer = null
})
</script>

<template>
  <div class="map-wrapper">
    <div ref="mapRef" class="ol-map" />

    <div v-if="!preview" class="location-button">
      <Button size="small" square @click="handleLocation">
        <img
          src="@af-mobile-client-vue3/assets/img/component/location.png"
          class="location-icon"
          alt="定位"
        >
      </Button>
    </div>
    <div class="map-controls">
      <!-- 控制按钮 -->
      <div ref="controlToggleRef" class="control-toggle">
        <Button
          :type="showControls ? 'primary' : 'default'"
          size="small"
          square
          @click.stop="showControls = !showControls"
        >
          <img
            src="@af-mobile-client-vue3/assets/img/component/mapLayers.png"
            class="toggle-icon"
            :class="[{ active: showControls }]"
            alt="图层"
          >
        </Button>
      </div>

      <!-- 图层控制面板 -->
      <Transition name="panel-fade">
        <div v-show="showControls" ref="controlPanelsRef" class="control-panels" @click.stop>
          <!-- 底图切换 -->
          <div class="control-panel base-layer-control">
            <div class="control-title">
              <i class="van-icon van-icon-map-marked" /> 底图切换
            </div>
            <select v-model="currentMapType" @change="handleMapChange(currentMapType)">
              <option v-for="layer in layerOptions" :key="layer.value" :value="layer.value">
                {{ layer.text }}
              </option>
            </select>
          </div>

          <!-- 图层控制 -->
          <div v-if="wmsLayerStatus.length > 0" class="control-panel layer-control">
            <div class="control-title">
              <i class="van-icon van-icon-layers" /> 图层控制
            </div>
            <div class="layer-list">
              <div
                v-for="layer in wmsLayerStatus"
                :key="layer.id"
                class="layer-item"
                :class="{ active: layer.show }"
                @click="handleToggleWMSLayer(layer)"
              >
                <i class="van-icon" :class="layer.show ? 'van-icon-eye' : 'van-icon-closed-eye'" />
                <span>{{ layer.value }}</span>
              </div>
            </div>
          </div>

          <!-- 点位图层 -->
          <div v-if="pointLayerStatus.length > 0" class="control-panel layer-control">
            <div class="control-title">
              <i class="van-icon van-icon-location" /> 点位图层
            </div>
            <div class="layer-list">
              <div
                v-for="layer in pointLayerStatus"
                :key="layer.id"
                class="layer-item"
                :class="{ active: layer.show }"
                @click="handleTogglePointLayer(layer)"
              >
                <i class="van-icon" :class="layer.show ? 'van-icon-eye' : 'van-icon-closed-eye'" />
                <span>{{ layer.value }}</span>
              </div>
            </div>
          </div>

          <!-- 轨迹图层 -->
          <div v-if="trackLayerStatus.length > 0" class="control-panel layer-control">
            <div class="control-title">
              <i class="van-icon van-icon-location-o" /> 轨迹图层
            </div>
            <div class="layer-list">
              <div
                v-for="track in trackLayerStatus"
                :key="track.id"
                class="layer-item"
                :class="{ active: track.show }"
                @click="handleToggleTrackLayer(track)"
              >
                <i class="van-icon" :class="track.show ? 'van-icon-eye' : 'van-icon-closed-eye'" />
                <span>{{ track.name }}</span>
              </div>
            </div>
          </div>

          <!-- 多边形图层 -->
          <div v-if="polygonLayerStatus.length > 0" class="control-panel layer-control">
            <div class="control-title">
              <i class="van-icon van-icon-map-marked" /> 多边形图层
            </div>
            <div class="layer-list">
              <div
                v-for="layer in polygonLayerStatus"
                :key="layer.id"
                class="layer-item"
                :class="{ active: layer.show }"
                @click="handleTogglePolygonLayer(layer)"
              >
                <i class="van-icon" :class="layer.show ? 'van-icon-eye' : 'van-icon-closed-eye'" />
                <span>{{ layer.value }}</span>
              </div>
            </div>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<style lang="less" scoped>
.map-wrapper {
  position: relative;
  width: 100%;
  height: 100%;

  .ol-map {
    width: 100%;
    height: 100%;
    min-height: 200px;
    touch-action: none;

    &:active {
      cursor: grabbing;
    }
  }

  // 定位按钮样式
  .location-button {
    position: absolute;
    left: 0.5em;
    bottom: calc(0.5em + 30px);
    z-index: 1000;

    .van-button {
      width: 32px;
      height: 32px;
      padding: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      border: 1px solid #ebedf0;
      background: rgba(255, 255, 255, 0.9);
      backdrop-filter: blur(4px);
      box-shadow: none;

      &:hover {
        border-color: #1989fa;
        .location-icon {
          opacity: 1;
        }
      }

      .location-icon {
        width: 20px;
        height: 20px;
        opacity: 0.7;
        transition: all 0.3s;
      }
    }
  }

  .map-controls {
    position: absolute;
    right: 10px;
    top: 10px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 8px;

    .control-toggle {
      align-self: flex-end;

      .van-button {
        width: 32px;
        height: 32px;
        padding: 4px;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 1px solid #ebedf0;
        background: rgba(255, 255, 255, 0.9);
        backdrop-filter: blur(4px);
        box-shadow: none;

        &.van-button--primary {
          background: #1989fa;
          border-color: #1989fa;
        }

        .toggle-icon {
          width: 20px;
          height: 20px;
          transition: all 0.3s;
          opacity: 0.7;

          &.active {
            opacity: 1;
            filter: brightness(1) invert(1);
          }
        }

        &:hover {
          border-color: #1989fa;
          .toggle-icon {
            opacity: 1;
          }
        }
      }
    }

    .control-panels {
      background: white;
      border-radius: 8px;
      padding: 12px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      min-width: 160px;
      max-width: 180px;
      max-height: 80vh;
      overflow-y: auto;
      transform-origin: top right;

      .control-panel {
        & + .control-panel {
          margin-top: 12px;
          padding-top: 12px;
          border-top: 1px solid #f5f5f5;
        }

        .control-title {
          font-size: 13px;
          font-weight: 500;
          color: #323233;
          margin-bottom: 8px;
          display: flex;
          align-items: center;
          gap: 4px;

          .van-icon {
            font-size: 16px;
            color: #1989fa;
          }
        }

        select {
          width: 100%;
          padding: 6px 24px 6px 8px;
          font-size: 13px;
          border: 1px solid #dcdee0;
          border-radius: 4px;
          background-color: #f7f8fa;
          color: #323233;
          cursor: pointer;
          outline: none;
          appearance: none;
          -webkit-appearance: none;
          -moz-appearance: none;
          background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
          background-repeat: no-repeat;
          background-position: right 6px center;
          background-size: 12px;

          &:hover {
            border-color: #1989fa;
          }

          option {
            padding: 6px;
            background: white;
          }
        }

        .layer-list {
          display: flex;
          flex-direction: column;
          gap: 4px;

          .layer-item {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 6px 8px;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.3s;
            background: #f7f8fa;
            border: 1px solid transparent;

            .van-icon {
              font-size: 14px;
              color: #969799;
            }

            span {
              font-size: 13px;
              color: #323233;
              flex: 1;
            }

            &:hover {
              background: #f0f2f5;
              border-color: #dcdee0;
            }

            &.active {
              background: #e6f3ff;
              border-color: #1989fa;

              .van-icon {
                color: #1989fa;
              }

              span {
                color: #1989fa;
              }
            }
          }
        }
      }
    }
  }
}

// 图层面板过渡动画
.panel-fade-enter-active {
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.panel-fade-leave-active {
  transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}

.panel-fade-enter-from {
  opacity: 0;
  transform: scale(0.9) translateY(-10px);
}

.panel-fade-leave-to {
  opacity: 0;
  transform: scale(0.9) translateY(-10px);
}

// 比例尺样式
.ol-scale-line {
  position: absolute;
  left: 0.5em;
  bottom: 0.5em;
  background: rgba(255, 255, 255, 0.8);
  padding: 2px;
  border-radius: 4px;
}

.ol-scale-line-inner {
  border: 2px solid #333;
  border-top: none;
  color: #333;
  font-size: 12px;
  text-align: center;
  margin: 1px;
  will-change: contents, width;
  transition: width 0.25s;
}

// 移动端适配
@media screen and (max-width: 768px) {
  .map-controls {
    right: 8px;
    top: 8px;
    gap: 6px;

    .control-panels {
      padding: 10px;
      min-width: 140px;

      .control-panel {
        & + .control-panel {
          margin-top: 10px;
          padding-top: 10px;
        }

        .control-title {
          font-size: 12px;
          margin-bottom: 6px;

          .van-icon {
            font-size: 14px;
          }
        }

        select {
          padding: 4px 20px 4px 6px;
          font-size: 12px;
          background-size: 10px;
        }

        .layer-list {
          gap: 3px;

          .layer-item {
            padding: 4px 6px;

            .van-icon {
              font-size: 12px;
            }

            span {
              font-size: 12px;
            }
          }
        }
      }
    }
  }
}

// 比例尺样式
:deep(.ol-scale-line) {
  position: absolute;
  left: 0.5em;
  bottom: 0.5em;
  background: rgba(255, 255, 255, 0.8);
  padding: 2px;
  border-radius: 4px;
}

:deep(.ol-scale-line-inner) {
  border: 2px solid #333;
  border-top: none;
  color: #333;
  font-size: 12px;
  text-align: center;
  margin: 1px;
  will-change: contents, width;
  transition: width 0.25s;
}
</style>
