<template>
  <div id="addressSearchCombobox" style="padding: 4px 0;">
    <div style="display: flex; width: 100%;">
      <a-input-group style="flex: 1; display: flex;padding-top: 1px;" compact>
        <a-input
          v-model="addressInput"
          :read-only="readOnly || !this.addressObj.address"
          @change="change"
          style="flex: 1; min-width: 0;"
          placeholder="请选择地址信息"/>
        <a-button
          v-if="attr?.inputOnAfterName && attr?.inputOnAfterFunc"
          style="min-width: 4rem; max-width: 6rem;"
          type="primary"
          @click="emitFunc(attr?.inputOnAfterFunc, addressInput)">
          {{ attr?.inputOnAfterName }}
        </a-button>
      </a-input-group>
      <a-button
        style="flex-shrink: 0; margin-left: 0.2rem;"
        :type="attr?.inputOnAfterName ? 'primary' : ''"
        @click="visible = true">
        <IcMapIcon/>
        定位
      </a-button>
    </div>
    <a-modal
      v-model="visible"
      title="地址选择"
      @ok="selected"
      :zIndex="m_index"
      :destroyOnClose="true"
      width="70%"
    >
      <div class="certain-category-search-wrapper">
        <a-auto-complete
          style="right: 0px;width: 90%;z-index:2;margin-top:2%;"
          :disabled="!mapAutocomplete"
          class="certain-category-search"
          dropdown-class-name="certain-category-search-dropdown"
          option-label-prop="value"
          placeholder="输入地址关键字搜索"
          @search="fetchFunction"
          @select="onSelect"
        >
          <a-spin v-if="searching" slot="notFoundContent" size="small"/>
          <template slot="dataSource">
            <a-select-opt-group v-for="group in option" :key="group.title">
              <span
                slot="label">
                <a-icon type="bank"/> {{ group.title }}
              </span>
              <a-select-option
                v-for="ad in group.children"
                :key="ad.address+'['+ad.label+']'+' <' + ad.value + '>'"
              >
                <p class="addressName">
                  <a-icon type="environment"/>
                  <span v-html="highLight(ad.label, ad.word)"></span></p>
                <p v-if="ad.address !== '[]'" class="addressRemark">
                  {{ ad.address }}
                </p>
              </a-select-option>
            </a-select-opt-group>
          </template>
          <a-input>
            <a-icon slot="suffix" class="certain-category-icon" type="search"/>
          </a-input>
        </a-auto-complete>
      </div>
      <div id="addressSearchCombobox_map"/>
      <a-descriptions
        bordered
        class="asc_descriptions"
        :column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 1, xs: 1 }"
      >
        <a-descriptions-item label="坐标" span="2"><span>{{ addressObj.lng_lat }}</span></a-descriptions-item>
        <a-descriptions-item label="地址" span="2" v-if="attr.type != 'coordinateSearch'"><span>{{ addressObj.address }}</span></a-descriptions-item>
      </a-descriptions>
    </a-modal>
  </div>
</template>
<script>

import { GetGDMap } from '@vue2-client/utils/map-utils'
import { debounce } from 'ant-design-vue/lib/vc-table/src/utils'
import { mapState } from 'vuex'
import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
import IcMapIcon from '@vue2-client/base-client/components/common/AddressSearchCombobox/IcMapIcon.vue'

export default {
  name: 'AddressSearchCombobox',
  components: { IcMapIcon },
  data () {
    // 检索去抖
    this.fetchFunction = debounce(this.fetchFunction, 300)
    return {
      // 地址搜索联想
      mapAutocomplete: undefined,
      // 检索结果
      option: [],
      // 最后检索版本
      lastFetchId: 0,
      // 检索中
      searching: false,
      addressObj: {
        lng_lat: '',
        address: '',
      },
      visible: false,
      map: null,
      m_index: 1002,
      positionPicker: null,
      addressInput: this.searchResult,
      // 选择地址返回的行政区域
      divisions: {},
      // 生成地址串中省市区街道信息禁止修改
      readOnlyDivisions: false,
      // cur 位置
      curPosition: {},
      gaode_key: '',
      gaode_secret_key: '',
      searchFlag: false,
    }
  },
  computed: {
    ...mapState('setting', ['isMobile'])
  },
  props: {
    attr: {
      type: Object,
      default: () => {
      }
    },
    emitFunc: {
      type: Function,
      default: () => {
      }
    },
    searchResult: {
      type: String,
      default: undefined
    },
    // 返回数据格式
    searchResultType: {
      type: String,
      default: 'Default'
      // default: 'Array'
      // default: 'Object'
    },
    // Object 返回格式时 可以自定义key
    resultKeys: {
      type: Object,
      default: () => {
        return { address: 'address', coords: 'coords' }
      }
    },
    // 地图渲染初始值
    address: {
      type: Object,
      default: () => {
        return { }
      }
    },
    readOnly: {
      type: Boolean,
      default: false
    },
    // 省市区是否可以修改
    divisionsChange: {
      type: Boolean,
      default: false
    },
    // 无 coords 且 IP 定位失败时的兜底中心点，格式 'lng,lat'，不传则使用全国视图
    defaultCenter: {
      type: String,
      default: ''
    },
  },
  created () {
  },
  mounted () {
  },
  methods: {
    getGaoDeKey () {
      return new Promise((resolve) => {
        getConfigByName('gaoDeKeyConfig', 'af-system', (config) => {
          this.gaode_secret_key = config.gaode_secret_key
          this.gaode_key = config.gaode_key
          resolve()
        })
      })
    },
    change (value) {
      if (this.readOnlyDivisions && !this.divisionsChange) {
        // 如果省市区街道信息禁止修改
        if (this.addressInput.includes('省') && this.addressInput.includes('市') && this.divisions.str && !this.addressInput.startsWith(this.divisions.str)) {
          this.addressInput = this.addressObj.address
          this.$message.info('省市区街道信息请从选择新地址以变更')
          return
        }
      }
      debounce(() => {
        this.selected(null, this.addressInput)
      }, 200)()
    },
    initMap (aMap) {
      const COORDS_REG = /^-?\d+\.?\d*,\s*-?\d+\.?\d*$/
      const coordsKey = this.resultKeys?.coords ?? 'coords'
      const coordsStr = (this.address?.[coordsKey] ?? this.address?.coords ?? '').toString().trim()
      const hasValidCoords = coordsStr && COORDS_REG.test(coordsStr)
      const centerFromCoords = hasValidCoords ? coordsStr.split(',').map(s => parseFloat(s.trim())) : null

      // 有有效 coords 时直接使用；无 coords 时先用全国视图，后续通过 IP 定位更新
      const initialCenter = centerFromCoords || [105, 35]
      const initialZoom = centerFromCoords ? 10 : 4

      this.map = new (aMap).Map('addressSearchCombobox_map', {
        resizeEnable: true,
        center: initialCenter,
        zoom: initialZoom,
      })
      this.geocoder = new (aMap).Geocoder({
        radius: 500
      })
      this.map.addControl(new (aMap).ToolBar())

      const applyCenterAndFetchAddress = (lnglat) => {
        const center = typeof lnglat?.lng === 'number' ? lnglat : { lng: lnglat[0], lat: lnglat[1] }
        this.map.setCenter([center.lng, center.lat])
        if (this.marker) {
          this.marker.setPosition([center.lng, center.lat])
        }
        this.geocoder.getAddress(center, (status, result) => {
          if (status === 'complete' && result.info === 'OK') {
            const addressComponent = result.regeocode.addressComponent
            this.divisions.province = addressComponent.province
            this.divisions.city = addressComponent.city
            this.divisions.district = addressComponent.district
            this.divisions.township = addressComponent.township
            this.divisions.towncode = addressComponent.towncode
            this.divisions.street = addressComponent.street
            if (result.regeocode.aois && result.regeocode.aois.length) {
              this.divisions.area = result.regeocode.aois[0].name
            } else if (result.regeocode.pois && result.regeocode.pois.length) {
              this.divisions.area = result.regeocode.pois[0].name
            }
            this.divisions.str = `${addressComponent.province}${addressComponent.city}${addressComponent.district}${addressComponent.township}`
            if (this.divisions.str && result.regeocode.formattedAddress.startsWith(this.divisions.str)) {
              this.readOnlyDivisions = true
            }
            this.addressObj.address = result.regeocode.formattedAddress
            this.addressObj.lng_lat = `${center.lng},${center.lat}`
          }
        })
      }

      this.map.on('complete', () => {
        const center = this.map.getCenter()
        this.marker = new (aMap).Marker({
          position: [center.lng, center.lat],
          map: this.map
        })

        if (hasValidCoords) {
          // 已有 coords，直接获取地址
          this.geocoder.getAddress(center, (status, result) => {
            if (status === 'complete' && result.info === 'OK') {
              const addressComponent = result.regeocode.addressComponent
              this.divisions.province = addressComponent.province
              this.divisions.city = addressComponent.city
              this.divisions.district = addressComponent.district
              this.divisions.township = addressComponent.township
              this.divisions.towncode = addressComponent.towncode
              this.divisions.street = addressComponent.street
              if (result.regeocode.aois && result.regeocode.aois.length) {
                this.divisions.area = result.regeocode.aois[0].name
              } else if (result.regeocode.pois && result.regeocode.pois.length) {
                this.divisions.area = result.regeocode.pois[0].name
              }
              this.divisions.str = `${addressComponent.province}${addressComponent.city}${addressComponent.district}${addressComponent.township}`
              if (this.divisions.str && result.regeocode.formattedAddress.startsWith(this.divisions.str)) {
                this.readOnlyDivisions = true
              }
              this.addressObj.address = result.regeocode.formattedAddress
              this.addressObj.lng_lat = `${center.lng},${center.lat}`
            }
          })
        } else {
          // 无 coords 时通过 IP 定位到用户所在城市
          const tryFallback = () => {
            const fallback = (this.defaultCenter || '').trim()
            if (fallback && COORDS_REG.test(fallback)) {
              const [lng, lat] = fallback.split(',').map(s => parseFloat(s.trim()))
              applyCenterAndFetchAddress([lng, lat])
              this.map.setZoom(10)
            } else {
              applyCenterAndFetchAddress(center)
            }
          }
          const citySearch = new (aMap).CitySearch()
          citySearch.getLocalCity((status, result) => {
            if (status === 'complete' && result?.info === 'OK' && result?.city) {
              this.geocoder.getLocation(result.city, (geoStatus, geoResult) => {
                if (geoStatus === 'complete' && geoResult?.geocodes?.length) {
                  const loc = geoResult.geocodes[0].location
                  applyCenterAndFetchAddress([loc.lng, loc.lat])
                  this.map.setZoom(10)
                } else {
                  tryFallback()
                }
              })
            } else {
              tryFallback()
            }
          })
        }
      })

      // 添加地图点击事件
      this.map.on('click', (e) => {
        const lnglat = e.lnglat
        this.curPosition = lnglat

        // 更新标记位置
        this.marker.setPosition([lnglat.lng, lnglat.lat])

        // 将地图中心移动到点击位置
        this.map.setCenter([lnglat.lng, lnglat.lat])

        // 获取点击位置的地址信息
        this.geocoder.getAddress(lnglat, (status, result) => {
          if (status === 'complete' && result.info === 'OK') {
            const addressComponent = result.regeocode.addressComponent
            this.divisions.province = addressComponent.province
            this.divisions.city = addressComponent.city
            this.divisions.district = addressComponent.district
            this.divisions.township = addressComponent.township
            this.divisions.towncode = addressComponent.towncode
            this.divisions.street = addressComponent.street

            // 获取小区信息
            if (result.regeocode.aois && result.regeocode.aois.length) {
              this.divisions.area = result.regeocode.aois[0].name
            } else if (result.regeocode.pois && result.regeocode.pois.length) {
              this.divisions.area = result.regeocode.pois[0].name
            }

            this.divisions.str = `${addressComponent.province}${addressComponent.city}${addressComponent.district}${addressComponent.township}`

            if (this.divisions.str && result.regeocode.formattedAddress.startsWith(this.divisions.str)) {
              this.readOnlyDivisions = true
            }

            if (!this.searchFlag) {
              this.addressObj.address = result.regeocode.formattedAddress
              this.addressObj.lng_lat = `${lnglat.lng},${lnglat.lat}`
            } else {
              this.searchFlag = false
            }
          }
        })
      })
    },
    onSelect (value) {
      // 选中地址后不触发更新坐标
      this.searchFlag = true
      const addressRegex = /^(.+?)\[/ // 匹配地址部分
      const labelRegex = /\[(.+?)\]/ // 匹配标签部分
      const coordinatesRegex = /<(.+?)>/ // 匹配坐标部分

      const addressMatch = value.match(addressRegex)
      const labelMatch = value.match(labelRegex)
      const coordinatesMatch = value.match(coordinatesRegex)

      this.addressObj.address = addressMatch ? addressMatch[1].trim() : null
      const label = labelMatch ? labelMatch[1].trim() : null
      this.addressObj.address = this.addressObj.address != null ? this.mergeAddressAndLabel(this.addressObj.address, label) : labelMatch[1].trim()
      this.addressObj.lng_lat = coordinatesMatch ? coordinatesMatch[1] : ''

      // 更新地图中心和标记位置
      const [lng, lat] = this.addressObj.lng_lat.split(',')
      this.map.setCenter([lng, lat])
      this.marker.setPosition([lng, lat])
    },
    mergeAddressAndLabel (address, label = '') {
      // 从地址的末尾开始匹配标签的开头部分
      for (let i = 0; i < label.length; i++) {
        const substring = label.substring(0, i + 1)
        if (address.endsWith(substring)) {
          // 找到最大匹配部分，拼接不重复的部分
          return address + label.substring(i + 1)
        }
      }
      // 如果没有重复部分，直接拼接
      return address + label
    },
    selected (_value, _addstr) {
      let result
      if (this.searchResultType !== 'Default') {
        if (this.searchResultType === 'Array') {
          result = [_addstr || this.addressObj.address, this.addressObj.lng_lat]
        } else {
          result = {}
          // 如果是坐标搜索，只返回坐标
          // 不知道为什么这么加 命名之前的组件已经返回了 lng_lat  ，算了 还是相信后人的智慧吧
          if (this.attr.type === 'coordinateSearch') {
            result[this.resultKeys.address] = this.addressObj.lng_lat
          } else {
            result[this.resultKeys.address] = _addstr || this.addressObj.address
            result[this.resultKeys.coords] = this.addressObj.lng_lat
          }
        }
      }
      this.visible = false
      this.$emit('onSelect', JSON.stringify(result))
      // 如果传递了地址 是为了更新上级 不做省市区检验处理
      if (!_addstr) {
        // 如果是坐标搜索，输入框显示坐标
        if (this.attr.type === 'coordinateSearch') {
          this.addressInput = this.addressObj.lng_lat
        } else {
          this.addressInput = this.addressObj.address
        }
        this.$emit('onDivisionsChange', Object.assign({}, result, { divisions: this.divisions, vm: this }))
      }
    },
// 懒加载检索方法
    fetchFunction (value) {
      if (value && this.mapAutocomplete) {
        this.searching = true
        this.lastFetchId += 1
        const fetchId = this.lastFetchId
        this.mapAutocomplete.search(value, (status, result) => {
          if (fetchId !== this.lastFetchId) {
            return
          }
          if (status === 'complete') {
            this.option = []
            const addressMap = {}
            const addressDisMap = {}
            result.tips.forEach((res) => {
              if (res.location) {
                if (!Object.prototype.hasOwnProperty.call(addressMap, res.district)) {
                  addressMap[res.district] = []
                }
                const msgData = {
                  label: res.name,
                  value: res.location.lng + ',' + res.location.lat,
                  address: res.address + '',
                  word: value
                }
                const p1 = [this.curPosition.lng, this.curPosition.lat]
                const p2 = [res.location.lng, res.location.lat]
                // 判断与当前位置的距离
                let dis = 0
                try {
                  // eslint-disable-next-line
                  dis = AMap.GeometryUtil.distance(p1, p2)
                } catch (e) {
                  console.log(e)
                }
                addressDisMap[res.district] = {}
                addressDisMap[res.district].dis = dis
                addressMap[res.district].push(msgData)
              }
            })
            for (const key of Object.keys(addressMap)) {
              this.option.push({
                title: key,
                dis: addressDisMap[key].dis,
                children: addressMap[key]
              })
            }
            // 根据 dis 排序
            this.option.sort((a, b) => {
              return a.dis - b.dis
            })
          }
          this.searching = false
        })
      }
    },
// 获取数据
    getData (value, callback) {
      if (value !== '') {
        const logicName = this.attr.keyName
        const logic = logicName.substring(6)
        runLogic(logic, value, 'af-system').then(res => {
          callback(res)
        })
      }
    },
// 关键字高亮
    highLight (value, word) {
      // 如果标题中包含，关键字就替换一下
      if (value.includes(word)) {
        value = value.replace(word,
          // 这里是替换成html格式的数据，最好再加一个样式权重，保险一点
          '<span style="color:red!important;">' + word + '</span>'
        )
        return value
        // eslint-disable-next-line brace-style
      }
      // 不包含的话还用这个
      else {
        return value
      }
    }
  },
  watch: {
    'visible' (val) {
      if (val) {
        this.$nextTick(async () => {
          if (!this.gaode_key) {
            await this.getGaoDeKey()
          }
          GetGDMap(this.gaode_secret_key, this.gaode_key).then(aMap => {
            this.initMap(aMap)
            this.mapAutocomplete = new (aMap).AutoComplete({
              city: '全国',
              dragEnable: true,
              animateEnable: false
            })
          })
        })
      }
    },
  }
}
</script>

<style lang="less" scoped>
#addressSearchCombobox_map {
  margin: 1% 0;
  width: 100%;
  height: 50vh;
  text-align: center
}

.address_btn_logo {
  width: 1rem;
  margin-right: 0.5rem
}

.asc_descriptions .ant-descriptions-item-content {
  width: 80%;
}

.certain-category-search-wrapper {
  margin-bottom: -4rem;
  text-align: center;
}

#addressSearchCombobox {

  .certain-category-search-dropdown .ant-select-dropdown-menu-item-group-title {
    color: #666;
    font-weight: bold;
    font-size: 14px;
  }

  .certain-category-search-dropdown .ant-select-dropdown-menu-item-group {
    border-bottom: 1px solid #f6f6f6;
  }

  .certain-category-search-dropdown .ant-select-dropdown-menu-item {
    padding-left: 16px;
  }

  .certain-category-search-dropdown .ant-select-dropdown-menu-item.show-all {
    text-align: center;
    cursor: default;
  }

  .certain-category-search-dropdown .ant-select-dropdown-menu {
    max-height: 300px;
  }

  .certain-category-search-wrapper .certain-search-item-count {
    position: absolute;
    color: #999;
    right: 16px;
  }

  .certain-category-search-wrapper .certain-category-search.ant-select-focused,
  .certain-category-icon {
    color: #108ee9;
  }

  .certain-category-search-wrapper .certain-category-icon {
    color: #6e6e6e;
    transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
    font-size: 16px;
  }

  .addressName {
    margin-bottom: 0;
    font-size: 14px;
    font-weight: bold;
  }

  .addressRemark {
    margin-bottom: 0;
    font-size: 12px;
  }
}
</style>
