<template>
  <div class="map-controls-root">
    <div v-if="showLayerDirectory" class="map-controls-anchor map-controls-anchor--top-left" aria-live="polite">
      <LayerDirectoryControl />
    </div>
    <div :class="['map-controls-anchor map-controls-anchor--bottom-right', bottomRightClass]" :style="bottomRightStyle"
      aria-live="polite">
      <div class="map-controls__stack">
        <div v-if="hasBottomRightControls" class="map-controls">
          <button v-if="showZoomControl" class="map-controls__btn" type="button" title="放大" aria-label="放大"
            @click="zoomIn" :disabled="isMapIdle">
            <el-icon>
              <Plus />
            </el-icon>
          </button>
          <button v-if="showZoomControl" class="map-controls__btn" type="button" title="缩小" aria-label="缩小"
            @click="zoomOut" :disabled="isMapIdle">
            <el-icon>
              <Minus />
            </el-icon>
          </button>
          <button v-if="showHomeControl" class="map-controls__btn map-controls__btn--home" type="button" title="返回初始视图"
            aria-label="返回初始视图" @click="goHome" :disabled="!canGoHome">
            <el-icon>
              <HomeFilled />
            </el-icon>
          </button>
        </div>
        <div v-if="showBaseMapSwitcher" class="map-controls">
          <BaseMapSwitcher />
        </div>
        <div v-if="showSceneModeToggle" class="map-controls">
          <SceneModeToggle />
        </div>
        <!-- 地图指南针 - 与其他控件垂直对齐 -->
        <div v-if="showCompass" class="map-controls">
          <MapCompass :visible="showCompass" :theme="compassTheme" />
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, shallowRef, watch } from 'vue'
import { useRoute } from 'vue-router'
import { HomeFilled, Minus, Plus } from '@element-plus/icons-vue'
import useMapStore from '@/map/stores/mapStore'
import useMapUiStore from '@/map/stores/mapUiStore'
import LayerDirectoryControl from './LayerDirectoryControl.vue'
import BaseMapSwitcher from './BaseMapSwitcher.vue'
import SceneModeToggle from './SceneModeToggle.vue'
import MapCompass from './MapCompass.vue'

const route = useRoute()
const mapStore = useMapStore()
const mapUiStore = useMapUiStore()

const DEFAULT_MAP_CONTROLS = Object.freeze({
  layout: {
    bottomRight: {
      style: {},
    },
  },
  bottomRight: [
    { id: 'zoom', order: 1 },
    { id: 'home', order: 2 },
  ],
  components: {
    layerDirectory: { visible: true },
    baseMapSwitcher: { visible: true },
    sceneModeToggle: { visible: true },
    compass: { visible: true, theme: 'light' },
  },
})

const SUPPORTED_CONTROLS = new Set(['zoom', 'home'])

const camera = shallowRef(null)
let detachReadyListener = null

const resolveCamera = () => {
  try {
    camera.value = mapStore.services().camera
  } catch (err) {
    camera.value = null
  }
}

const mergePositionControls = (baseList = [], overrideList) => {
  const map = new Map()

  baseList.forEach((item, idx) => {
    if (!item || typeof item.id !== 'string') return
    map.set(item.id, {
      ...item,
      order: typeof item.order === 'number' ? item.order : idx + 1,
    })
  })

  if (Array.isArray(overrideList)) {
    overrideList.forEach((item, idx) => {
      if (!item || typeof item.id !== 'string') return
      if (item.visible === false) {
        map.delete(item.id)
        return
      }
      const existing = map.get(item.id)
      const nextOrder = typeof item.order === 'number'
        ? item.order
        : existing && typeof existing.order === 'number'
          ? existing.order
          : baseList.length + idx + 1
      map.set(item.id, {
        ...existing,
        ...item,
        id: item.id,
        order: nextOrder,
      })
    })
  }

  return Array.from(map.values())
    .filter((item) => item.visible !== false)
    .sort((a, b) => {
      const aOrder = typeof a.order === 'number' ? a.order : 0
      const bOrder = typeof b.order === 'number' ? b.order : 0
      return aOrder - bOrder
    })
}

const mergeLayouts = (baseLayout = {}, overrideLayout = {}) => {
  const result = {}
  const positions = new Set([
    ...Object.keys(baseLayout || {}),
    ...Object.keys(overrideLayout || {}),
  ])

  positions.forEach((position) => {
    const baseEntry = baseLayout?.[position] || {}
    const overrideEntry = overrideLayout?.[position] || {}
    const combinedClass = [baseEntry.class, overrideEntry.class].filter(Boolean).join(' ')
    const style = { ...(baseEntry.style || {}), ...(overrideEntry.style || {}) }
    const entry = {}
    if (combinedClass) entry.class = combinedClass
    if (Object.keys(style).length) entry.style = style
    if (Object.keys(entry).length) {
      result[position] = entry
    } else if (baseLayout?.[position] || overrideLayout?.[position]) {
      result[position] = {}
    }
  })

  return result
}

const mergeMapControlsConfig = (baseConfig = {}, overrideConfig = {}) => {
  const result = {}

  // Merge layout configuration
  const baseLayout = baseConfig.layout || {}
  const overrideLayout = overrideConfig.layout || {}
  const layout = mergeLayouts(baseLayout, overrideLayout)
  if (Object.keys(layout).length) {
    result.layout = layout
  }

  // Merge components configuration (generic approach)
  const baseComponents = baseConfig.components || {}
  const overrideComponents = overrideConfig.components || {}
  if (Object.keys(baseComponents).length || Object.keys(overrideComponents).length) {
    result.components = {}
    const componentKeys = new Set([...Object.keys(baseComponents), ...Object.keys(overrideComponents)])
    componentKeys.forEach((key) => {
      result.components[key] = {
        ...baseComponents[key],
        ...overrideComponents[key],
      }
    })
  }

  // Handle position-based controls (bottomRight, topLeft, etc.)
  const excludedKeys = new Set(['layout', 'components'])
  const baseKeys = Object.keys(baseConfig || {}).filter((key) => !excludedKeys.has(key))
  const overrideKeys = Object.keys(overrideConfig || {}).filter((key) => !excludedKeys.has(key))
  const positions = new Set([...baseKeys, ...overrideKeys])

  positions.forEach((position) => {
    result[position] = mergePositionControls(baseConfig?.[position], overrideConfig?.[position])
  })

  return result
}

const cloneConfig = (config) => mergeMapControlsConfig(config, {})

const resolvedConfig = computed(() => {
  const matchedConfigs = route.matched
    .map((record) => record.meta?.mapControls)
    .filter(Boolean)
  if (!matchedConfigs.length) {
    return cloneConfig(DEFAULT_MAP_CONTROLS)
  }
  return matchedConfigs.reduce(
    (acc, config) => mergeMapControlsConfig(acc, config),
    cloneConfig(DEFAULT_MAP_CONTROLS)
  )
})

const bottomRightControls = computed(() => {
  const list = resolvedConfig.value.bottomRight || []
  return list
    .filter((item) => SUPPORTED_CONTROLS.has(item.id))
    .filter((item) => mapUiStore.isControlVisible(item.id))
})

const bottomRightLayout = computed(() => resolvedConfig.value.layout?.bottomRight || {})
const bottomRightClass = computed(() => bottomRightLayout.value.class)
const bottomRightStyle = computed(() => bottomRightLayout.value.style)

const showZoomControl = computed(() => bottomRightControls.value.some((item) => item.id === 'zoom'))
const showHomeControl = computed(() => bottomRightControls.value.some((item) => item.id === 'home'))
const hasBottomRightControls = computed(() => bottomRightControls.value.length > 0)

// 控件显隐判定
const isComponentEnabledInRoute = (componentName) => {
  return resolvedConfig.value.components?.[componentName]?.visible !== false
}

const resolveComponentVisibility = (componentName) => {
  if (!isComponentEnabledInRoute(componentName)) return false
  return mapUiStore.isControlVisible(componentName)
}

const showLayerDirectory = computed(() => resolveComponentVisibility('layerDirectory'))
const showBaseMapSwitcher = computed(() => resolveComponentVisibility('baseMapSwitcher'))
const showSceneModeToggle = computed(() => resolveComponentVisibility('sceneModeToggle'))
const showCompass = computed(() => resolveComponentVisibility('compass'))

// 指南针主题配置
const compassTheme = computed(() => {
  const compassConfig = resolvedConfig.value.components?.compass || {}
  return compassConfig.theme || 'light'
})


onMounted(() => {
  resolveCamera()
  detachReadyListener = mapStore.onReady(() => {
    resolveCamera()
  })
})

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

watch(
  () => mapStore.ready,
  (ready) => {
    if (ready) {
      resolveCamera()
    } else {
      camera.value = null
    }
  }
)

const isMapReady = computed(() => mapStore.ready && !!camera.value)
const isMapIdle = computed(() => !isMapReady.value)
const canGoHome = computed(() => isMapReady.value && !!mapStore.homeView)

function zoomIn() {
  if (!isMapReady.value) return
  camera.value.zoomIn()
}

function zoomOut() {
  if (!isMapReady.value) return
  camera.value.zoomOut()
}

async function goHome() {
  if (!canGoHome.value) return
  try {
    await camera.value.flyToHome()
  } catch (err) {
    console.warn('flyToHome failed', err)
  }
}
</script>

<style scoped lang="scss">
.map-controls-root {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
}

.map-controls-anchor {
  position: absolute;
  pointer-events: none;
}

.map-controls-anchor--bottom-right {
  right: 24px;
  bottom: 24px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}

.map-controls-anchor--top-left {
  left: 16px;
  top: 60px;
}

.map-controls__stack {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  pointer-events: none;
}

.map-controls__stack>* {
  pointer-events: auto;
}

.map-controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
  pointer-events: auto;
}

.map-controls__btn {
  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: 18px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.map-controls__btn:not(:disabled):hover {
  background: #ffffff;
  transform: translateY(-1px);
}

.map-controls__btn:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.map-controls__btn--home {
  font-size: 20px;
}


@media (max-width: 768px) {
  .map-controls-anchor--bottom-right {
    right: 12px;
    bottom: 16px;
  }

  .map-controls__btn {
    width: 36px;
    height: 36px;
  }
}
</style>
