<template>
  <div class="position-picker-mobile">
    <van-field
      :model-value="modelValue"
      is-link
      readonly
      :label="label"
      :placeholder="placeholder"
      @click="openPopup"
    />

    <van-popup
      v-model:show="showPopup"
      position="right"
      class="position-picker-popup"
      @opened="handlePopupOpened"
    >
      <div class="picker-screen">
        <van-nav-bar
          title="坐标点选取"
          left-text="返回"
          left-arrow
          fixed
          placeholder
          @click-left="showPopup = false"
        />

        <div class="picker-toolbar">
          <div class="coord-grid">
            <div class="coord-card">
              <span class="coord-label">经度</span>
              <span class="coord-value">{{ displayLongitude || '--' }}</span>
            </div>
            <div class="coord-card">
              <span class="coord-label">纬度</span>
              <span class="coord-value">{{ displayLatitude || '--' }}</span>
            </div>
          </div>

          <div class="search-row">
            <van-search
              v-model="searchKeyword"
              shape="round"
              class="search-box"
              :placeholder="searchPlaceholder"
              @search="handleSearch"
            />
            <van-button
              type="primary"
              block
              class="search-btn"
              :loading="searching"
              @click="handleSearch"
            >
              搜索
            </van-button>
          </div>

          <div v-if="draftAddress" class="address-tip">
            <span class="address-label">地点</span>
            <span class="address-text">{{ draftAddress }}</span>
          </div>
        </div>

        <div class="map-wrapper">
          <div ref="mapContainerRef" class="map-container"></div>
          <div v-if="mapError" class="map-overlay map-overlay--error">
            <p>{{ mapError }}</p>
            <van-button type="primary" size="small" @click="retryMapLoad">重新加载</van-button>
          </div>
          <div v-else-if="!mapReady" class="map-overlay">地图加载中...</div>
        </div>

        <div class="bottom-bar">
          <div class="bottom-tip">点击地图或拖动标记点后，再确认位置</div>
          <van-button type="primary" block round @click="handleConfirm">确认位置</van-button>
        </div>
      </div>
    </van-popup>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import axios from 'axios'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'
import markerIcon from 'leaflet/dist/images/marker-icon.png'
import markerShadow from 'leaflet/dist/images/marker-shadow.png'
import { showToast } from 'vant'

const AMAP_CONFIG = {
  webServiceKey: 'c30e9ebd414fd6a4dfcc1ba8c2060dbb',
  baseUrl: 'https://restapi.amap.com',
  timeout: 10000
}

const DEFAULT_CENTER = {
  longitude: 106.551643,
  latitude: 29.563761
}

delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
  iconRetinaUrl: markerIcon2x,
  iconUrl: markerIcon,
  shadowUrl: markerShadow
})

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  label: {
    type: String,
    default: ''
  },
  placeholder: {
    type: String,
    default: '请选择位置'
  },
  searchPlaceholder: {
    type: String,
    default: '请输入地点名称'
  },
  initialLongitude: {
    type: [String, Number],
    default: null
  },
  initialLatitude: {
    type: [String, Number],
    default: null
  }
})

const emit = defineEmits(['update:modelValue', 'change'])

const showPopup = ref(false)
const mapReady = ref(false)
const mapError = ref('')
const searching = ref(false)
const searchKeyword = ref('')
const mapContainerRef = ref(null)

const selectedLongitude = ref(null)
const selectedLatitude = ref(null)
const selectedAddress = ref('')
const draftLongitude = ref(null)
const draftLatitude = ref(null)
const draftAddress = ref('')

let mapInstance = null
let markerInstance = null
let reverseGeocodeToken = 0

const normalizeCoordinate = (value) => {
  if (value === '' || value === null || value === undefined) {
    return null
  }

  const normalized = Number(value)
  return Number.isFinite(normalized) ? normalized : null
}

const displayLongitude = computed(() => {
  return draftLongitude.value === null ? '' : draftLongitude.value.toFixed(6)
})

const displayLatitude = computed(() => {
  return draftLatitude.value === null ? '' : draftLatitude.value.toFixed(6)
})

const currentCenter = () => {
  const longitude = selectedLongitude.value ?? normalizeCoordinate(props.initialLongitude) ?? DEFAULT_CENTER.longitude
  const latitude = selectedLatitude.value ?? normalizeCoordinate(props.initialLatitude) ?? DEFAULT_CENTER.latitude
  return { longitude, latitude }
}

const resolveInitialSelection = () => {
  if (selectedLongitude.value !== null && selectedLatitude.value !== null) {
    return {
      longitude: selectedLongitude.value,
      latitude: selectedLatitude.value
    }
  }

  const longitude = normalizeCoordinate(props.initialLongitude)
  const latitude = normalizeCoordinate(props.initialLatitude)
  if (longitude !== null && latitude !== null) {
    return { longitude, latitude }
  }

  return null
}

const syncDraftFromSelected = () => {
  const initialSelection = resolveInitialSelection()
  draftLongitude.value = initialSelection?.longitude ?? null
  draftLatitude.value = initialSelection?.latitude ?? null
  draftAddress.value = selectedAddress.value || props.modelValue || ''
  searchKeyword.value = draftAddress.value
}

const setMarkerPosition = (longitude, latitude, shouldPan = true) => {
  if (!mapInstance || longitude === null || latitude === null) {
    return
  }

  const latLng = [latitude, longitude]
  if (!markerInstance) {
    markerInstance = L.marker(latLng, {
      draggable: true
    }).addTo(mapInstance)

    markerInstance.on('dragend', () => {
      const markerLatLng = markerInstance.getLatLng()
      updateDraftPosition(markerLatLng.lng, markerLatLng.lat, true)
    })
  } else {
    markerInstance.setLatLng(latLng)
  }

  if (shouldPan) {
    mapInstance.setView(latLng, Math.max(mapInstance.getZoom(), 15))
  }
}

const reverseGeocode = async (longitude, latitude) => {
  const token = ++reverseGeocodeToken

  try {
    const response = await axios.get(`${AMAP_CONFIG.baseUrl}/v3/geocode/regeo`, {
      params: {
        key: AMAP_CONFIG.webServiceKey,
        location: `${longitude},${latitude}`,
        extensions: 'base',
        output: 'json'
      },
      timeout: AMAP_CONFIG.timeout
    })

    if (token !== reverseGeocodeToken) {
      return
    }

    if (response.data.status !== '1') {
      throw new Error(response.data.info || '逆地理编码失败')
    }

    const formattedAddress = response.data.regeocode?.formatted_address || ''
    draftAddress.value = formattedAddress
    searchKeyword.value = formattedAddress
  } catch (_error) {
    if (token !== reverseGeocodeToken) {
      return
    }

    draftAddress.value = draftAddress.value || `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`
  }
}

const updateDraftPosition = (longitude, latitude, shouldResolveAddress = false) => {
  draftLongitude.value = Number(longitude.toFixed(6))
  draftLatitude.value = Number(latitude.toFixed(6))
  setMarkerPosition(draftLongitude.value, draftLatitude.value, false)

  if (shouldResolveAddress) {
    reverseGeocode(draftLongitude.value, draftLatitude.value)
  }
}

const initMap = async () => {
  mapError.value = ''
  mapReady.value = false

  try {
    await nextTick()

    if (!mapContainerRef.value) {
      return
    }

    const center = currentCenter()

    if (!mapInstance) {
      mapInstance = L.map(mapContainerRef.value, {
        zoomControl: true,
        attributionControl: false
      }).setView([center.latitude, center.longitude], 13)

      L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
        subdomains: ['1', '2', '3', '4'],
        maxZoom: 18,
        minZoom: 3
      }).addTo(mapInstance)

      mapInstance.on('click', (event) => {
        updateDraftPosition(event.latlng.lng, event.latlng.lat, true)
      })
    } else {
      mapInstance.setView([center.latitude, center.longitude], mapInstance.getZoom() || 13)
    }

    if (draftLongitude.value !== null && draftLatitude.value !== null) {
      setMarkerPosition(draftLongitude.value, draftLatitude.value)
    } else if (markerInstance) {
      markerInstance.remove()
      markerInstance = null
    }

    mapReady.value = true
    requestAnimationFrame(() => {
      mapInstance?.invalidateSize()
    })

    if (!draftAddress.value && draftLongitude.value !== null && draftLatitude.value !== null) {
      reverseGeocode(draftLongitude.value, draftLatitude.value)
    }
  } catch (error) {
    console.error('地图初始化失败:', error)
    mapError.value = '地图初始化失败，请稍后重试'
  }
}

const openPopup = () => {
  syncDraftFromSelected()
  showPopup.value = true
}

const handlePopupOpened = async () => {
  await initMap()
  requestAnimationFrame(() => {
    mapInstance?.invalidateSize()
  })
}

const handleSearch = async () => {
  const keyword = searchKeyword.value.trim()
  if (!keyword) {
    showToast('请输入地点名称')
    return
  }

  searching.value = true
  try {
    const response = await axios.get(`${AMAP_CONFIG.baseUrl}/v3/geocode/geo`, {
      params: {
        key: AMAP_CONFIG.webServiceKey,
        address: keyword,
        output: 'json'
      },
      timeout: AMAP_CONFIG.timeout
    })

    if (response.data.status !== '1') {
      throw new Error(response.data.info || '地点搜索失败')
    }

    const geocode = response.data.geocodes?.[0]
    if (!geocode?.location) {
      throw new Error('未找到对应地点')
    }

    const [longitude, latitude] = geocode.location.split(',').map(Number)
    draftAddress.value = geocode.formatted_address || keyword
    searchKeyword.value = draftAddress.value
    updateDraftPosition(longitude, latitude)
    setMarkerPosition(longitude, latitude)
  } catch (error) {
    console.error('地点搜索失败:', error)
    showToast(error.message || '地点搜索失败，请重试')
  } finally {
    searching.value = false
  }
}

const retryMapLoad = () => {
  initMap()
}

const handleConfirm = () => {
  if (draftLongitude.value === null || draftLatitude.value === null) {
    showToast('请先在地图上选择位置')
    return
  }

  selectedLongitude.value = draftLongitude.value
  selectedLatitude.value = draftLatitude.value
  selectedAddress.value = draftAddress.value || searchKeyword.value.trim() || `${draftLatitude.value}, ${draftLongitude.value}`

  emit('update:modelValue', selectedAddress.value)
  emit('change', {
    longitude: selectedLongitude.value,
    latitude: selectedLatitude.value,
    address: selectedAddress.value
  })

  showPopup.value = false
}

watch(
  () => props.modelValue,
  (value) => {
    if (!value) {
      return
    }

    selectedAddress.value = value
    if (!showPopup.value) {
      draftAddress.value = value
    }
  },
  { immediate: true }
)

onBeforeUnmount(() => {
  if (mapInstance) {
    mapInstance.remove()
    mapInstance = null
  }
})
</script>

<style scoped lang="scss">
.position-picker-mobile {
  width: 100%;
}

.position-picker-popup {
  width: 100%;
  height: 100%;
  background: #f7f8fa;
}

.picker-screen {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.picker-toolbar {
  padding: 12px 12px 10px;
  background: #fff;
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.06);
  z-index: 2;
}

.coord-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}

.coord-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px;
  border-radius: 12px;
  background: linear-gradient(180deg, #f8fbff 0%, #eef4ff 100%);
}

.coord-label {
  color: #6b7280;
  font-size: 12px;
}

.coord-value {
  color: #1f2937;
  font-size: 16px;
  font-weight: 600;
  word-break: break-all;
}

.search-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.search-box {
  flex: 1;
  padding: 0;
  background: transparent;
}

.search-btn {
  width: 88px;
  flex-shrink: 0;
}

.address-tip {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  color: #4b5563;
  font-size: 12px;
  line-height: 1.5;
}

.address-label {
  flex-shrink: 0;
  color: #2563eb;
  font-weight: 600;
}

.address-text {
  word-break: break-all;
}

.map-wrapper {
  position: relative;
  flex: 1;
  min-height: 280px;
  background: #eef2f7;
}

.map-container {
  width: 100%;
  height: 100%;
}

.map-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #606266;
  font-size: 14px;
  background: rgba(247, 248, 250, 0.9);
  z-index: 2;
}

.map-overlay--error {
  flex-direction: column;
  gap: 12px;
  color: #ee0a24;
}

.bottom-bar {
  padding: 10px 12px calc(12px + env(safe-area-inset-bottom));
  background: #fff;
  box-shadow: 0 -6px 18px rgba(15, 23, 42, 0.08);
}

.bottom-tip {
  margin-bottom: 10px;
  color: #86909c;
  font-size: 12px;
  text-align: center;
}

:deep(.van-nav-bar) {
  flex-shrink: 0;
}

:deep(.van-nav-bar__content) {
  background: #fff;
}

:deep(.leaflet-container) {
  font-family: inherit;
}
</style>
