<script setup lang="ts">
import { CircleCheck, Location, Plus, Search } from '@element-plus/icons-vue'
import { ElMessage, type AutocompleteInstance } from 'element-plus'

const ICON_CLASS = 'map-main-marker'

const emits = defineEmits(['done'])

const props = defineProps([
  'modelValue',
  'height',
  'center',
  'mapKey',
  'markerSrc',
  'zoom',
  'selectedZoom',
  'mapStyle',
  'poiType',
  'poiRadius',
  'poiKeywords',
  'okText',
  'required',
  'message',
  'suggestionCity'
])

let mapIns: any = null
// 地图容器
const mapRef = ref<any>()
const autocompleteRef = ref<AutocompleteInstance>()

// 是否是poi列表点击移动
const isItemClickMove = ref<boolean>(false)
// 图标样式
const centerIconClass = ref<Array<string>>([ICON_CLASS])

// 当前选中
const current = ref<any>(null)

// 是否加载中
const loading = ref<boolean>(false)

const poiData = ref<any[]>([])

// 关键字
const keywords = ref<string>('')
// 最后一次搜索关键字
const lastKeywords = ref<any>(null)

// 关键字搜索数据
const suggestionData = ref<any[]>([])

onMounted(() => {
  initMap()
})
/* 初始化地图 */
const initMap = () => {
  mapIns = new BMapGL.Map(mapRef.value) // 创建Map实例
  mapIns.centerAndZoom(new BMapGL.Point(props.center[0], props.center[1]), props.zoom) // 初始化地图,设置中心点坐标和地图级别
  mapIns.enableScrollWheelZoom(true) //开启鼠标滚轮缩放
  if (props.mapStyle) {
    mapIns.setMapStyleV2({ styleId: props.mapStyle })
  }

  mapIns.on('moveend', () => {
    if (isItemClickMove.value) {
      isItemClickMove.value = false
      return
    }
    bounceIcon()
    const { lng, lat } = mapIns.getCenter()
    searchPOI(lng, lat)
  })

  // 第一次加载
  searchPOI(props.center[0], props.center[1])
}

/* poi 搜索 */
const searchPOI = (lng: number, lat: number) => {
  loading.value = true
  current.value = null
  searchNearBy(lng, lat)
    .then(res => {
      loading.value = false
      poiData.value = res
    })
    .catch(() => {
      loading.value = false
    })
}

/* 设置地图中心点 */
const setMapCenter = (lng: number, lat: number, zoom: number) => {
  if (lng && lat) {
    if (zoom == null) {
      mapIns.setCenter([lng, lat])
    } else {
      mapIns.flyTo(new BMapGL.Point(lng, lat), zoom)
    }
  }
}

/* poi 点击 */
const handleItemClick = (item: any) => {
  const { lng, lat } = item
  setMapCenter(lng, lat, props.selectedZoom)
  isItemClickMove.value = true
  current.value = item
}

/* 让地图图标跳动 */
const bounceIcon = () => {
  nextTick(() => {
    centerIconClass.value = [ICON_CLASS]
    setTimeout(() => {
      centerIconClass.value = [ICON_CLASS, 'map-main-marker-bounce']
    }, 0)
  })
}

/* 确认选择 */
const handleConfirm = () => {
  // 是否强制选择
  if (props.required && !current.value) {
    ElMessage.error(props.message)
    return
  }
  // 如果右侧不选择，则返回当前中心点
  const { lng, lat } = mapIns.getCenter()
  emits('done', current.value || { lng, lat })
}

/* 关键字搜索 */
const handleSelect = (keyword: string, callback?: any) => {
  if (!keyword || lastKeywords.value === keyword) {
    callback && callback(suggestionData.value)
    return
  }
  lastKeywords.value = keyword
  searchKeywords(keyword)
    .then(result => {
      suggestionData.value = result
      callback && callback(suggestionData.value)
    })
    .catch((e: Error) => {
      console.error(e)
      callback && callback(suggestionData.value)
    })
}

/* 关键字搜索选中 */
const handleSelectSelect = (item: any) => {
  autocompleteRef.value && autocompleteRef.value.blur()
  const { lng, lat } = item
  setMapCenter(lng, lat, props.selectedZoom)
  bounceIcon()
}

/* 检索附近兴趣点 */
const searchNearBy = (lng: number, lat: number): Promise<any[]> => {
  return new Promise((resolve, reject) => {
    jsonp(
      `https://api.map.baidu.com/place/v2/search?query=${props.poiKeywords}&location=${lat},${lng}&radius=${props.poiRadius}&page_size=20&output=json&ak=${props.mapKey}`,
      (result: any) => {
        if (result.status === 0) {
          resolve(result.results.map((d: any) => formatPoi(d)))
        } else {
          reject(new Error(result.message))
          return
        }
      }
    )
  })
}

// 关键字检索
const searchKeywords = (keyword: string): Promise<any[]> => {
  return new Promise((resolve, reject) => {
    jsonp(
      `https://api.map.baidu.com/place/v2/suggestion?query=${keyword}&region=${props.suggestionCity}&city_limit=true&output=json&ak=${props.mapKey}`,
      (res: any) => {
        if (res.status === 0) {
          const tips = (res.result as any[]).filter(d => !!d.location)
          resolve(tips.map(d => formatPoi(d)))
        } else {
          reject(new Error(res.message))
        }
      }
    )
  })
}

const jsonp = (url: string, callback: Function) => {
  // 创建一个随机的回调函数名，避免命名冲突
  const callbackFuncName = 'jsonp_callback_' + Math.round(100000 * Math.random())
  // 将回调函数名添加到 URL 的查询参数中
  url += (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callbackFuncName
  // 创建一个全局的回调函数
  ;(window as any)[callbackFuncName] = function (data: any) {
    // 回调函数执行后，删除全局函数并释放资源
    delete (window as any)[callbackFuncName]
    document.body.removeChild(script)

    // 调用传入的回调函数处理数据
    callback(data)
  }
  // 创建一个 script 标签
  const script = document.createElement('script')
  script.src = url

  // 将 script 标签插入到页面中，发起 JSONP 请求
  document.body.appendChild(script)
}

/* 格式化返回的点位 */
const formatPoi = (item: any) => {
  return {
    ...item,
    lng: item.location.lng,
    lat: item.location.lat
  }
}

onBeforeUnmount(() => {
  /* 销毁地图 */
  mapIns && mapIns.destroy()
  mapIns = null
  current.value = null
  loading.value = false
  poiData.value = []
})
</script>

<template>
  <div class="map-container">
    <div class="map-main">
      <div ref="mapRef" :style="{ height }"></div>
      <el-icon class="map-icon-plus" color="var(--el-color-primary)">
        <Plus />
      </el-icon>
      <img :src="markerSrc" :class="centerIconClass" />
      <el-autocomplete
        v-model="keywords"
        valueKey="name"
        :clearable="true"
        :fetch-suggestions="handleSelect"
        placeholder="请输入关键字进行搜索"
        @select="handleSelectSelect"
        class="map-keywrod-input"
        ref="autocompleteRef"
      >
        <template #prefix>
          <el-icon class="el-input__icon">
            <Search />
          </el-icon>
        </template>
      </el-autocomplete>
    </div>
    <div class="map-poi-side">
      <div class="map-poi-list" v-loading="loading">
        <div class="map-poi-item" v-for="item in poiData" :key="item.id" @click="handleItemClick(item)">
          <div class="map-poi-icon">
            <el-icon size="18">
              <Location />
            </el-icon>
          </div>
          <div class="map-poi-desc">
            <div class="map-poi-title">{{ item.name }}</div>
            <div class="map-poi-address">{{ item.address }}</div>
          </div>
          <div class="map-poi-icon map-poi-icon-checked">
            <el-icon size="18" color="var(--el-color-primary)" v-if="current === item">
              <CircleCheck />
            </el-icon>
          </div>
        </div>
      </div>
      <div class="map-poi-button">
        <el-button type="primary" @click="handleConfirm" style="width: 100%">{{ okText }}</el-button>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss"></style>
