import { ref, onBeforeUnmount } from 'vue'
import { ARCGIS_CSS, ARCGIS_INIT, BASEMAP_URL, WKID, DEFAULT_EXTENT, TBA_BASIN_PATHS } from '@/utils/gis-config'

let arcgisLoaded = false
let arcgisLoading = null

function loadArcGIS() {
  if (arcgisLoaded) return Promise.resolve()
  if (arcgisLoading) return arcgisLoading

  arcgisLoading = new Promise((resolve, reject) => {
    if (document.querySelector(`link[href="${ARCGIS_CSS}"]`)) {
      arcgisLoaded = true
      resolve()
      return
    }

    const link = document.createElement('link')
    link.rel = 'stylesheet'
    link.href = ARCGIS_CSS
    document.head.appendChild(link)

    if (window.require) {
      arcgisLoaded = true
      resolve()
      return
    }

    const script = document.createElement('script')
    script.src = ARCGIS_INIT
    script.async = true
    script.onload = () => {
      arcgisLoaded = true
      resolve()
    }
    script.onerror = () => {
      arcgisLoading = null
      reject(new Error('ArcGIS JS API 加载失败'))
    }
    document.head.appendChild(script)
  })

  return arcgisLoading
}

function requireModules(modules) {
  return loadArcGIS().then(() => {
    return new Promise((resolve, reject) => {
      try {
        window.require(modules, function (...args) {
          resolve(args)
        })
      } catch (e) {
        reject(e)
      }
    })
  })
}

export function useArcGisMap() {
  const map = ref(null)
  const view = ref(null)
  const coord = ref({ lng: 0, lat: 0 })

  let MapConstructor = null
  let MapViewConstructor = null
  let TileLayer = null
  let GraphicsLayer = null
  let Graphic = null
  let Point = null
  let SpatialReference = null
  let SimpleMarkerSymbol = null
  let SimpleLineSymbol = null
  let Polyline = null
  let Extent = null

  async function ensureModules() {
    if (MapConstructor) return
    const [
      Map,
      MapView,
      TileLayerCls,
      GraphicsLayerCls,
      GraphicCls,
      PointCls,
      SpatialRefCls,
      SimpleMarkerSymbolCls,
      SimpleLineSymbolCls,
      PolylineCls,
      ExtentCls,
    ] = await requireModules([
      'esri/Map',
      'esri/views/MapView',
      'esri/layers/TileLayer',
      'esri/layers/GraphicsLayer',
      'esri/Graphic',
      'esri/geometry/Point',
      'esri/geometry/SpatialReference',
      'esri/symbols/SimpleMarkerSymbol',
      'esri/symbols/SimpleLineSymbol',
      'esri/geometry/Polyline',
      'esri/geometry/Extent',
    ])
    MapConstructor = Map
    MapViewConstructor = MapView
    TileLayer = TileLayerCls
    GraphicsLayer = GraphicsLayerCls
    Graphic = GraphicCls
    Point = PointCls
    SpatialReference = SpatialRefCls
    SimpleMarkerSymbol = SimpleMarkerSymbolCls
    SimpleLineSymbol = SimpleLineSymbolCls
    Polyline = PolylineCls
    Extent = ExtentCls
  }

  async function getModule(path) {
    await ensureModules()
    return new Promise(resolve => window.require([path], m => resolve(m)))
  }

  async function initMap(containerId, center, zoom) {
    await ensureModules()

    const basemapLayer = new TileLayer({
      url: BASEMAP_URL,
    })

    const mapInstance = new MapConstructor({
      layers: [basemapLayer],
    })

    const viewInstance = new MapViewConstructor({
      map: mapInstance,
      container: containerId,
      popup: {
        dockEnabled: false,
        dockOptions: { buttonEnabled: false, breakpoint: false },
      },
    })

    viewInstance.when(() => {
      const ext = new Extent({
        ...DEFAULT_EXTENT,
        spatialReference: new SpatialReference({ wkid: WKID }),
      })
      viewInstance.extent = ext
      if (center) {
        viewInstance.center = center
      }
      if (zoom) {
        viewInstance.zoom = zoom
      }
    })

    viewInstance.on('pointer-move', (event) => {
      const point = viewInstance.toMap({ x: event.x, y: event.y })
      coord.value = { lng: point.longitude, lat: point.latitude }
    })

    map.value = mapInstance
    view.value = viewInstance

    return { map: mapInstance, view: viewInstance }
  }

  function createGraphicsLayer(id) {
    return new GraphicsLayer({ id })
  }

  function createPointMarker(lat, lng, symbol, attributes) {
    const point = new Point({
      longitude: lng,
      latitude: lat,
      spatialReference: new SpatialReference({ wkid: WKID }),
    })
    return new Graphic({
      geometry: point,
      symbol: symbol || createDefaultSymbol(),
      attributes: attributes || {},
    })
  }

  function createCircleSymbol(color, radius, opacity) {
    return new SimpleMarkerSymbol({
      style: 'circle',
      color: [parseInt(color.slice(1, 3), 16), parseInt(color.slice(3, 5), 16), parseInt(color.slice(5, 7), 16), opacity != null ? opacity : 204],
      outline: { color: [255, 255, 255], width: 1.5 },
      size: radius || 14,
    })
  }

  function createDefaultSymbol() {
    return new SimpleMarkerSymbol({
      style: 'circle',
      color: [64, 158, 255, 204],
      outline: { color: [255, 255, 255], width: 1.5 },
      size: 14,
    })
  }

  function openPopup(viewInstance, title, content, location) {
    viewInstance.popup.open({
      title,
      content,
      location: location || viewInstance.center,
    })
  }

  async function addBasinBoundary() {
    if (!map.value || !Polyline || !SimpleLineSymbol || !Graphic) return
    const paths = TBA_BASIN_PATHS.map(p => ({ longitude: p[0], latitude: p[1] }))
    const polyline = new Polyline({
      paths: [TBA_BASIN_PATHS],
      spatialReference: new SpatialReference({ wkid: WKID }),
    })
    const symbol = new SimpleLineSymbol({
      color: [72, 149, 239, 180],
      width: 2,
      style: 'solid',
    })
    const graphic = new Graphic({ geometry: polyline, symbol })
    const layerId = 'tba-basin-boundary'
    let layer = map.value.findLayerById(layerId)
    if (!layer) {
      layer = new GraphicsLayer({ id: layerId })
      map.value.add(layer)
    }
    layer.add(graphic)
  }

  function destroy() {
    if (view.value) {
      view.value.destroy()
      view.value = null
    }
    if (map.value) {
      map.value.destroy()
      map.value = null
    }
  }

  onBeforeUnmount(() => {
    destroy()
  })

  return {
    map,
    view,
    coord,
    initMap,
    ensureModules,
    getModule,
    createGraphicsLayer,
    createPointMarker,
    createCircleSymbol,
    createDefaultSymbol,
    openPopup,
    addBasinBoundary,
    destroy,
  }
}
