<template>
  <button
    class="scene-mode-toggle"
     
    type="button"
    :disabled="!canToggle"
    :title="buttonTitle"
    @click="toggleSceneMode"
    aria-live="polite"
  >
    {{ buttonLabel }}
  </button>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
import * as Cesium from 'cesium'
import useMapStore from '@/map/stores/mapStore'


const mapStore = useMapStore()
const sceneRef = shallowRef(null)
const cameraService = shallowRef(null)
const layerService = shallowRef(null)
const is3DMode = ref(true)
const isMorphing = ref(false)
const pendingRestoreState = shallowRef(null)
let detachReadyListener = null
let detachMorphStartListener = null
let detachMorphCompleteListener = null

/**
 * 清理由 Cesium 场景注册的监听。
 * @returns {void} 无返回值
 */
function cleanupScene() {
  if (typeof detachMorphStartListener === 'function') {
    detachMorphStartListener()
  }
  if (typeof detachMorphCompleteListener === 'function') {
    detachMorphCompleteListener()
  }
  detachMorphStartListener = null
  detachMorphCompleteListener = null
  sceneRef.value = null
  is3DMode.value = true
  isMorphing.value = false
  pendingRestoreState.value = null
}

/**
 * 同步场景模式状态，更新按钮显示。
 * @param {Cesium.Scene | null} scene Cesium 场景
 * @returns {void} 无返回值
 */
function syncSceneMode(scene) {
  if (!scene) {
    is3DMode.value = true
    return
  }
  const mode = scene.mode
  is3DMode.value = mode !== Cesium.SceneMode.SCENE2D
}

/**
 * 解析并缓存地图相关服务。
 * @returns {void} 无返回值
 */
function resolveServices() {
  if (!mapStore.ready) {
    cameraService.value = null
    layerService.value = null
    return
  }
  try {
    const { camera, layer } = mapStore.services()
    cameraService.value = camera
    layerService.value = layer
  } catch (error) {
    console.warn('解析地图服务失败', error)
    cameraService.value = null
    layerService.value = null
  }
}

/**
 * 构造相机快照。
 * @param {Cesium.Camera | null} camera Cesium 相机
 * @returns {object | null} 相机视角参数
 */
function buildCameraSnapshot(camera) {
  if (!camera) return null
  const cartographic = camera.positionCartographic
  if (!cartographic) return null
  return {
    lon: Cesium.Math.toDegrees(cartographic.longitude),
    lat: Cesium.Math.toDegrees(cartographic.latitude),
    height: cartographic.height,
    heading: Cesium.Math.toDegrees(camera.heading || 0),
    pitch: Cesium.Math.toDegrees(camera.pitch || 0),
    roll: Cesium.Math.toDegrees(camera.roll || 0),
  }
}
/**
 * 矩形转经纬度对象。
 * @param {Cesium.Rectangle} rectangle Cesium 矩形
 * @returns {{ west:number, south:number, east:number, north:number }} 经纬度范围
 */
function rectangleToDegrees(rectangle) {
  return {
    west: Cesium.Math.toDegrees(rectangle.west),
    south: Cesium.Math.toDegrees(rectangle.south),
    east: Cesium.Math.toDegrees(rectangle.east),
    north: Cesium.Math.toDegrees(rectangle.north),
  }
}

/**
 * 记录当前场景状态（相机、可视范围、图层）。
 * @param {Cesium.Viewer} viewer Cesium Viewer
 * @returns {object | null} 场景快照
 */
function captureSceneSnapshot(viewer) {
  if (!viewer) return null
  const snapshot = {
    cameraView: null,
    viewRectangle: null,
    imageryOrder: [],
    vectorOrder: [],
    layerStates: {},
  }

  try {
    if (cameraService.value && typeof cameraService.value.getCurrentView === 'function') {
      snapshot.cameraView = cameraService.value.getCurrentView()
    } else {
      snapshot.cameraView = buildCameraSnapshot(viewer.camera)
    }
  } catch (error) {
    console.warn('获取相机视角失败', error)
    snapshot.cameraView = buildCameraSnapshot(viewer.camera)
  }

  try {
    const rectangle = viewer.camera.computeViewRectangle(viewer.scene?.globe?.ellipsoid)
    if (rectangle) {
      snapshot.viewRectangle = rectangleToDegrees(rectangle)
    }
  } catch (error) {
    console.warn('计算可视范围失败', error)
  }

  const layerEntries = Object.entries(mapStore.layers || {})
  const objectLookup = new Map()
  layerEntries.forEach(([id, record]) => {
    if (!record) return
    if (record.obj) {
      objectLookup.set(record.obj, { id, record })
    }
    snapshot.layerStates[id] = {
      show: record.show,
      opacity: typeof record.opacity === 'number' ? record.opacity : null,
      type: record.type,
      splitDirection: record.type === 'imagery' && record.obj ? record.obj.splitDirection : undefined,
    }
  })

  try {
    const imageryLayers = viewer.imageryLayers
    const imageryOrder = []
    for (let i = 0; i < imageryLayers.length; i += 1) {
      const layer = imageryLayers.get(i)
      const info = objectLookup.get(layer)
      if (info) imageryOrder.push(info.id)
    }
    snapshot.imageryOrder = imageryOrder
  } catch (error) {
    console.warn('记录影像图层顺序失败', error)
  }

  try {
    const dataSources = viewer.dataSources
    const vectorOrder = []
    for (let i = 0; i < dataSources.length; i += 1) {
      const dataSource = dataSources.get(i)
      const info = objectLookup.get(dataSource)
      if (info) vectorOrder.push(info.id)
    }
    snapshot.vectorOrder = vectorOrder
  } catch (error) {
    console.warn('记录矢量图层顺序失败', error)
  }

  return snapshot
}

/**
 * 还原图层显隐、顺序等状态。
 * @param {Cesium.Viewer} viewer Cesium Viewer
 * @param {object | null} snapshot 场景快照
 * @returns {void} 无返回值
 */
function applyLayerState(viewer, snapshot) {
  if (!snapshot) return
  const stateMap = snapshot.layerStates || {}

  if (layerService.value) {
    Object.entries(stateMap).forEach(([id, info]) => {
      if (!info) return
      try {
        if (info.show != null) layerService.value.showLayer(id, info.show)
        if (info.type === 'imagery' && typeof info.opacity === 'number') {
          layerService.value.setOpacity(id, info.opacity)
        }
      } catch (error) {
        console.warn(`恢复图层状态失败: ${id}`, error)
      }
    })
  } else {
    Object.entries(stateMap).forEach(([id, info]) => {
      if (!info) return
      const record = mapStore.layers?.[id]
      if (!record || !record.obj) return
      if (info.show != null) {
        record.obj.show = !!info.show
        record.show = !!info.show
      }
      if (info.type === 'imagery' && typeof info.opacity === 'number') {
        record.obj.alpha = info.opacity
        record.opacity = info.opacity
      }
    })
  }

  const imageryOrder = Array.isArray(snapshot.imageryOrder) ? snapshot.imageryOrder : []
  if (imageryOrder.length) {
    imageryOrder.slice().reverse().forEach((id) => {
      const info = stateMap[id]
      const record = mapStore.layers?.[id]
      if (!record || record.type !== 'imagery' || !record.obj) return
      try {
        if (layerService.value && typeof layerService.value.moveLayer === 'function') {
          layerService.value.moveLayer(id, 'bottom')
        } else if (viewer?.imageryLayers?.contains(record.obj)) {
          viewer.imageryLayers.lowerToBottom(record.obj)
        }
        if (info && info.splitDirection != null) {
          record.obj.splitDirection = info.splitDirection
        }
      } catch (error) {
        console.warn(`恢复影像图层顺序失败: ${id}`, error)
      }
    })
  } else {
    Object.entries(stateMap).forEach(([id, info]) => {
      if (!info || info.splitDirection == null) return
      const record = mapStore.layers?.[id]
      if (record?.type === 'imagery' && record.obj) {
        record.obj.splitDirection = info.splitDirection
      }
    })
  }

  const vectorOrder = Array.isArray(snapshot.vectorOrder) ? snapshot.vectorOrder : []
  if (vectorOrder.length) {
    vectorOrder.slice().reverse().forEach((id) => {
      const record = mapStore.layers?.[id]
      if (!record || !(record.type === 'vector' || record.type === 'datasource') || !record.obj) return
      try {
        if (layerService.value && typeof layerService.value.moveLayer === 'function') {
          layerService.value.moveLayer(id, 'bottom')
        } else if (viewer?.dataSources?.contains(record.obj)) {
          viewer.dataSources.lowerToBottom(record.obj)
        }
      } catch (error) {
        console.warn(`恢复矢量图层顺序失败: ${id}`, error)
      }
    })
  }
}

/**
 * 还原相机视角与可视范围。
 * @param {Cesium.Scene} scene Cesium 场景
 * @param {Cesium.Viewer} viewer Cesium Viewer
 * @param {object | null} snapshot 场景快照
 * @returns {void} 无返回值
 */
function applyCameraState(scene, viewer, snapshot) {
  if (!snapshot) return
  const cameraView = snapshot.cameraView
  try {
    if (scene.mode === Cesium.SceneMode.SCENE2D) {
      if (snapshot.viewRectangle) {
        const rect = Cesium.Rectangle.fromDegrees(
          snapshot.viewRectangle.west,
          snapshot.viewRectangle.south,
          snapshot.viewRectangle.east,
          snapshot.viewRectangle.north,
        )
        viewer.camera.setView({ destination: rect })
      } else if (cameraView) {
        viewer.camera.setView({
          destination: Cesium.Cartesian3.fromDegrees(
            Number(cameraView.lon) || 0,
            Number(cameraView.lat) || 0,
            Number(cameraView.height) || 1500,
          ),
        })
      }
    } else if (cameraView) {
      if (cameraService.value && typeof cameraService.value.setView === 'function') {
        cameraService.value.setView(cameraView)
      } else {
        viewer.camera.setView({
          destination: Cesium.Cartesian3.fromDegrees(
            Number(cameraView.lon) || 0,
            Number(cameraView.lat) || 0,
            Number(cameraView.height) || 1500,
          ),
          orientation: {
            heading: Cesium.Math.toRadians(Number(cameraView.heading) || 0),
            pitch: Cesium.Math.toRadians(Number(cameraView.pitch) || 0),
            roll: Cesium.Math.toRadians(Number(cameraView.roll) || 0),
          },
        })
      }
    }

    if (viewer?.scene?.requestRender) {
      viewer.scene.requestRender()
    }
  } catch (error) {
    console.warn('恢复相机视角失败', error)
  }
}
/**
 * 场景模式切换完成后恢复快照状态。
 * @param {Cesium.Scene} scene Cesium 场景
 * @returns {void} 无返回值
 */
function restoreSceneAfterMorph(scene) {
  const payload = pendingRestoreState.value
  pendingRestoreState.value = null
  if (!payload || !mapStore.ready) return

  resolveServices()

  let viewer = null
  try {
    viewer = mapStore.getViewer()
  } catch (error) {
    console.warn('恢复场景失败，未获取到 Viewer', error)
    return
  }

  if (!viewer) return
  applyLayerState(viewer, payload.snapshot)
  applyCameraState(scene, viewer, payload.snapshot)
}

/**
 * 挂载场景监听，感知模式切换。
 * @param {Cesium.Scene | null} scene Cesium 场景
 * @returns {void} 无返回值
 */
function attachScene(scene) {
  if (sceneRef.value === scene) {
    syncSceneMode(scene)
    return
  }

  if (typeof detachMorphStartListener === 'function') {
    detachMorphStartListener()
  }
  if (typeof detachMorphCompleteListener === 'function') {
    detachMorphCompleteListener()
  }

  sceneRef.value = scene
  if (!scene) {
    syncSceneMode(null)
    return
  }

  syncSceneMode(scene)

  const handleMorphStart = () => {
    isMorphing.value = true
  }
  const handleMorphComplete = () => {
    isMorphing.value = false
    syncSceneMode(scene)
    restoreSceneAfterMorph(scene)
  }

  scene.morphStart.addEventListener(handleMorphStart)
  scene.morphComplete.addEventListener(handleMorphComplete)
  detachMorphStartListener = () => {
    scene.morphStart.removeEventListener(handleMorphStart)
  }
  detachMorphCompleteListener = () => {
    scene.morphComplete.removeEventListener(handleMorphComplete)
  }
}

/**
 * 从 Store 中解析并挂载场景及依赖服务。
 * @returns {void} 无返回值
 */
function resolveSceneFromStore() {
  if (!mapStore.ready) {
    cleanupScene()
    resolveServices()
    return
  }
  try {
    resolveServices()
    const viewer = mapStore.getViewer()
    attachScene(viewer?.scene ?? null)
  } catch (error) {
    console.warn('解析 Cesium 场景失败', error)
    cleanupScene()
  }
}

/**
 * 切换 Cesium 二维与三维模式，并记录快照。
 * @returns {void} 无返回值
 */
function toggleSceneMode() {
  if (!mapStore.ready || isMorphing.value) return

  let viewer = null
  try {
    viewer = mapStore.getViewer()
  } catch (error) {
    console.warn('切换模式失败，未获取到 Viewer', error)
    return
  }
  if (!viewer) return

  resolveServices()

  const scene = viewer.scene
  const targetIs3D = !is3DMode.value
  const snapshot = captureSceneSnapshot(viewer)
  pendingRestoreState.value = { snapshot }

  is3DMode.value = targetIs3D
  isMorphing.value = true

  try {
    if (targetIs3D) {
      scene.morphTo3D(0.6)
    } else {
      scene.morphTo2D(0.6)
    }
  } catch (error) {
    console.error('切换场景模式失败', error)
    isMorphing.value = false
    syncSceneMode(scene)
    restoreSceneAfterMorph(scene)
  }
}

const buttonLabel = computed(() => (is3DMode.value ? '2D' : '3D'))
const buttonTitle = computed(() => (is3DMode.value ? '切换到二维模式' : '切换到三维模式'))
const canToggle = computed(() => !!sceneRef.value && !isMorphing.value)

onMounted(() => {
  if (mapStore.ready) {
    resolveSceneFromStore()
  }
  detachReadyListener = mapStore.onReady(() => {
    resolveSceneFromStore()
  })
})

onBeforeUnmount(() => {
  cleanupScene()
  if (typeof detachReadyListener === 'function') {
    detachReadyListener()
  }
  detachReadyListener = null
})

watch(
  () => mapStore.ready,
  (ready) => {
    if (ready) {
      resolveSceneFromStore()
    } else {
      cleanupScene()
      resolveServices()
    }
  }
)
</script>

<style scoped lang="scss">
.scene-mode-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.92);
  color: #1f1f1f;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  pointer-events: auto;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.scene-mode-toggle:not(:disabled):hover {
  background: #ffffff;
  transform: translateY(-1px);
}

.scene-mode-toggle:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

@media (max-width: 768px) {
  .scene-mode-toggle {
    width: 36px;
    height: 36px;
    font-size: 13px;
  }
}
</style>
