<template>
  <GuidePageFrame
    :active-tab="currentTab"
    variant="overlay"
    :top-tabs-variant="currentTab === 'guide' ? 'segmented' : 'underline'"
    :top-tabs-top="currentTab === 'guide' ? '80px' : '0'"
    :show-top-tabs="currentTab === 'guide'"
    @tab-change="handleTabChange"
  >
    <GuideMapShell
      v-if="currentTab === 'guide'"
      :search-text="guideSearchText"
      :active-mode="guideShellMode"
      :mode-layout="'status'"
      :mode-status="guideStatusLabel"
      mode-status-tone="glass"
      search-top="20px"
      search-variant="home"
      :show-mode-row="false"
      mode-top="80px"
      :map-type="guideMapType"
      :outdoor-variant="guideOutdoorVariant"
      :indoor-asset-base-url="indoorNavAssetBaseUrl"
      :indoor-model-source="indoorModelSource"
      :floors="guideFloors"
      :normalize-floor-id="normalizeGuideFloorId"
      indoor-initial-view="floor"
      :indoor-view="indoorView"
      :layer-mode="indoorLayerMode"
      :active-floor="activeGuideFloor"
      layer-mode-toggle-top="154px"
      floor-top="420px"
      floor-side="left"
      show-floor-header
      :show-layer-mode-toggle="false"
      :show-floor="is3DMode"
      tools-top="130px"
      show-zoom-controls
      zoom-controls-top="560px"
      show-location-control
      location-control-bottom="54px"
      show-more-control
      more-control-bottom="54px"
      :tools="guide3DTools"
      :route-preview="activeRoutePreview"
      :show-route="Boolean(activeRoutePreview)"
      @search-tap="handleGuideSearchTap"
      @mode-change="handleModeChange"
      @floor-change="handleFloorChange"
      @indoor-view-change="handleIndoorViewChange"
      @poi-click="handleGuidePoiClick"
      @selection-clear="handleGuideSelectionClear"
      @tool-click="handleGuideToolClick"
      @more-click="handleGuideMoreClick"
      @auto-switch="handleAutoSwitch"
    >
      <template #overlay>
        <view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-tip">
          <text class="entrance-tip-text">主入口 约 4 分钟</text>
        </view>
      </template>

      <view
        v-if="selectedGuidePoi && !showRoutePlanner && !isSimulatingRoute"
        class="guide-poi-card"
        :class="{ collapsed: isPoiCardCollapsed }"
      >
        <view class="poi-card-handle" @tap.stop="togglePoiCardCollapsed"></view>
        <view v-if="isPoiCardCollapsed" class="poi-card-collapsed" @tap="expandPoiCard">
          <view class="poi-card-copy">
            <text class="poi-card-title">{{ selectedGuidePoi.name }}</text>
            <text class="poi-card-subtitle">位置预览 · 点按展开操作</text>
          </view>
          <view class="poi-card-mini-action">
            <text class="poi-card-mini-text">展开</text>
          </view>
        </view>
        <template v-else>
          <view class="poi-card-header">
            <view class="poi-card-copy">
              <text class="poi-card-title">{{ selectedGuidePoi.name }}</text>
              <text class="poi-card-subtitle">{{ selectedGuidePoiSubtitle }}</text>
            </view>
            <view class="poi-card-header-actions">
              <view class="poi-card-soft-action" @tap="collapsePoiCard">
                <text class="poi-card-soft-text">收起</text>
              </view>
              <view class="poi-card-close" @tap="handleGuideSelectionClear">
                <text class="poi-card-close-text">×</text>
              </view>
            </view>
          </view>
          <view class="poi-card-actions">
            <view class="poi-action primary" @tap="handlePreviewSelectedPoi">
              <text class="poi-action-text">查看位置</text>
            </view>
            <view class="poi-action" @tap="handleSetSelectedPoiAsStart">
              <text class="poi-action-text">从这里出发</text>
            </view>
            <view class="poi-action" @tap="handleSetSelectedPoiAsEnd">
              <text class="poi-action-text">去这里</text>
            </view>
            <view class="poi-action" @tap="handleSelectedPoiRelated">
              <text class="poi-action-text">相关讲解</text>
            </view>
          </view>
        </template>
      </view>

      <view v-if="showGuideMoreMenu && !showRoutePlanner" class="guide-more-popover">
        <view class="more-popover-handle"></view>
        <view class="more-popover-grid">
          <view class="more-action" @tap="handleMoreRouteGuide">
            <text class="more-action-title">线路预览</text>
            <text class="more-action-desc">室内到室内</text>
          </view>
          <view class="more-action" @tap="handleMoreOutdoorReference">
            <text class="more-action-title">室外参考</text>
            <text class="more-action-desc">入口位置</text>
          </view>
          <view class="more-action" @tap="handleMoreRecommendedEntrance">
            <text class="more-action-title">推荐入口</text>
            <text class="more-action-desc">入口参考</text>
          </view>
          <view class="more-action" @tap="handleEnter3DMode">
            <text class="more-action-title">室内3D</text>
            <text class="more-action-desc">位置预览</text>
          </view>
          <view class="more-action" @tap="handleMoreToggleLayer">
            <text class="more-action-title">{{ indoorLayerMode === 'multi' ? '单层' : '多层' }}</text>
            <text class="more-action-desc">切换楼层</text>
          </view>
        </view>
      </view>

      <RoutePlannerPanel
        v-if="is3DMode"
        :visible="showRoutePlanner"
        :start-point="routeStartPoint"
        :end-point="routeEndPoint"
        :point-options="routePointOptions"
        :has-route-preview="Boolean(activeRoutePreview)"
        :loading="routePlanning"
        :error="routePanelError"
        :summary="routePlanSummary"
        :picker-loading="routeTargetsLoading"
        :picker-error="routeTargetsError"
        @update:start-point="handleRouteStartPointChange"
        @update:end-point="handleRouteEndPointChange"
        @picker-open="handleRoutePickerOpen"
        @search="handleRoutePickerSearch"
        @swap="handleRouteSwap"
        @clear="handleRouteClear"
        @view-route="handleRouteView"
        @simulate-guide="handleRouteSimulate"
      />

      <view v-if="isSimulatingRoute && activeRoutePreview" class="route-sim-top-pill">
        <text class="route-sim-top-text">
          {{ activeRoutePreview.start.name }} → {{ activeRoutePreview.end.name }}
        </text>
      </view>

      <view v-if="isSimulatingRoute && activeRoutePreview" class="route-sim-step-bar">
        <text class="route-sim-step-text">{{ routeSimulationStepText }}</text>
        <view class="route-sim-expand" @tap="showRoutePlanner = true">
          <text class="route-sim-expand-text">展开预览</text>
        </view>
      </view>

      <view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-bottom-card">
        <text class="entrance-card-title">主入口</text>
        <text class="entrance-card-desc">
          可参考主入口位置，也可手动进入室内3D并选择楼层/区域。
        </text>
        <view class="entrance-actions">
          <view class="entrance-btn secondary" @tap="handleSwitchEntrance">
            <text class="entrance-btn-text">切换入口</text>
          </view>
          <view class="entrance-btn primary" @tap="handleEnter3DMode">
            <text class="entrance-btn-text">进入室内3D</text>
          </view>
        </view>
      </view>
    </GuideMapShell>

    <view v-else-if="currentTab === 'explain'" class="explain-page">
      <ExplainHallSelect
        :halls="explainHallItems"
        :loading="explainLoading"
        :error="explainError"
        @hall-click="handleExplainHallClick"
        @back="handleExplainBack"
      />
    </view>
  </GuidePageFrame>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
import RoutePlannerPanel from '@/components/navigation/RoutePlannerPanel.vue'
import type {
  RoutePointOption
} from '@/components/navigation/RoutePointPicker.vue'
import ExplainHallSelect, {
  type ExplainHallSelectItem
} from '@/components/explain/ExplainHallSelect.vue'
import {
  getLastGuideLocationPreviewUrl,
  isGuideTopTab,
  type GuideTopTab
} from '@/utils/guideTopTabs'
import {
  guideUseCase
} from '@/usecases/guideUseCase'
import {
  guideRouteUseCase
} from '@/usecases/guideRouteUseCase'
import {
  explainUseCase
} from '@/usecases/explainUseCase'
import type {
  GuideRenderPoi
} from '@/domain/guideModel'
import type {
  GuideRouteResult,
  GuideRouteTarget,
  MuseumExhibit,
  MuseumFloor
} from '@/domain/museum'

type GuideIndoorView = 'overview' | 'floor' | 'multi'
type LayerDisplayMode = 'single' | 'multi'

const isIndexHashRoute = () => {
  if (typeof window === 'undefined') return false

  const hash = window.location.hash || ''
  return (
    !hash
    || hash === '#'
    || hash === '#/'
    || hash.startsWith('#/?')
    || hash.startsWith('#/pages/index/index')
  )
}

const topTabFromHash = () => {
  if (typeof window === 'undefined') return null
  if (!isIndexHashRoute()) return null

  const queryString = window.location.hash.split('?')[1] || ''
  const tab = new URLSearchParams(queryString).get('tab')
  return isGuideTopTab(tab) ? tab : null
}

const initialTopTab = (): GuideTopTab => topTabFromHash() || 'guide'

// 3D 模式状态
const is3DMode = ref(true)
const guideOutdoorState = ref<'home' | 'entrance'>('home')
const indoorView = ref<GuideIndoorView>('floor')
const activeGuideFloor = ref('1F')
const selectedGuidePoi = ref<GuideRenderPoi | null>(null)

// 状态
const currentTab = ref<GuideTopTab>(initialTopTab())
const showRoutePlanner = ref(false)
const routeStartPoint = ref<RoutePointOption | null>(null)
const routeEndPoint = ref<RoutePointOption | null>(null)
const routePointOptions = ref<RoutePointOption[]>([])
const routeTargetsLoading = ref(false)
const routeTargetsError = ref('')
const routePlanning = ref(false)
const routePlanError = ref('')
const routePreviewUnavailableMessage = ref('')
const activeRoutePreview = ref<GuideRouteResult | null>(null)
const showGuideMoreMenu = ref(false)
const isSimulatingRoute = ref(false)
const isPoiCardCollapsed = ref(false)

const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
const indoorModelSource = guideUseCase.getModelSource()
const guideFloors = ref<MuseumFloor[]>([])
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
const indoorLayerMode = computed<LayerDisplayMode>(() => (
  indoorView.value === 'multi' ? 'multi' : 'single'
))
const guide3DTools = computed(() => {
  return is3DMode.value ? ['重置', '俯视', '斜视', '清除', '清路线'] : []
})

const routePlanSummary = computed(() => {
  if (activeRoutePreview.value) {
    const floorCount = new Set(activeRoutePreview.value.floorSegments.map((segment) => segment.floorId)).size
    const floorText = floorCount > 1 ? `跨 ${floorCount} 层` : activeRoutePreview.value.start.floorLabel
    return `${floorText} · 约 ${Math.round(activeRoutePreview.value.distanceMeters)} 米`
  }

  if (routeStartPoint.value && routeEndPoint.value) {
    return `${routeStartPoint.value.floorLabel} 到 ${routeEndPoint.value.floorLabel}`
  }

  return ''
})

const routePanelError = computed(() => routePlanError.value || routePreviewUnavailableMessage.value)

const toRoutePointOption = (target: GuideRouteTarget): RoutePointOption => ({
  poiId: target.poiId,
  name: target.name,
  floorId: target.floorId,
  floorLabel: target.floorLabel,
  categoryLabel: target.categoryLabel
})

const selectedGuidePoiSubtitle = computed(() => {
  const poi = selectedGuidePoi.value
  if (!poi) return ''
  const floor = guideFloors.value.find((item) => item.id === poi.floorId)
  const floorLabel = floor?.label || poi.floorId
  return `${floorLabel} · ${poi.primaryCategoryZh || '位置预览'}`
})

const routeSimulationStepText = computed(() => {
  if (!activeRoutePreview.value) return '路线示意 · 查看预览'
  const hasConnector = activeRoutePreview.value.connectorPoints.length > 0
  return hasConnector ? '路线示意 · 跨楼层连接点' : '路线示意 · 同层位置关系'
})

const explainHallItems = ref<ExplainHallSelectItem[]>([])
const explainLoading = ref(false)
const explainError = ref('')
let explainLoadPromise: Promise<void> | null = null

// Tab 切换处理
const syncTopTabToUrl = (tabId: GuideTopTab) => {
  if (typeof window === 'undefined') return
  if (!isIndexHashRoute()) return

  const url = `#/pages/index/index?tab=${tabId}`
  if (window.location.hash !== url) {
    window.history.replaceState(null, '', url)
  }
}

const loadTopTabData = (tabId: GuideTopTab) => {
  if (tabId === 'explain') {
    void loadExplainExhibits()
  } else {
    void loadGuideFloors()
  }
}

const handleTabChange = (tabId: GuideTopTab) => {
  if (tabId === 'guide' && currentTab.value === 'explain') {
    const lastLocationPreviewUrl = getLastGuideLocationPreviewUrl()
    if (lastLocationPreviewUrl) {
      uni.reLaunch({
        url: lastLocationPreviewUrl
      })
      return
    }
  }

  currentTab.value = tabId
  syncTopTabToUrl(tabId)
  loadTopTabData(tabId)

  console.log('切换标签:', tabId)
  
  // 关闭可能打开的面板
  showGuideMoreMenu.value = false
  showRoutePlanner.value = false
  isSimulatingRoute.value = false
  isPoiCardCollapsed.value = false
}

const syncTopTabFromHash = () => {
  if (!isIndexHashRoute()) return

  const tab = topTabFromHash()
  if (tab) {
    const tabChanged = currentTab.value !== tab
    currentTab.value = tab
    loadTopTabData(tab)
    if (tabChanged) {
      showGuideMoreMenu.value = false
      showRoutePlanner.value = false
      isSimulatingRoute.value = false
      isPoiCardCollapsed.value = false
    }
    syncTopTabToUrl(tab)
    return
  }

  syncTopTabToUrl(currentTab.value)
  loadTopTabData(currentTab.value)
}

onMounted(() => {
  if (typeof window === 'undefined') return

  syncTopTabFromHash()
  window.addEventListener('hashchange', syncTopTabFromHash)
})

onUnmounted(() => {
  if (typeof window === 'undefined') return

  window.removeEventListener('hashchange', syncTopTabFromHash)
})

onLoad((options: any = {}) => {
  const routeTab = Array.isArray(options.tab) ? options.tab[0] : options.tab
  const tab = topTabFromHash() || routeTab
  if (isGuideTopTab(tab)) {
    currentTab.value = tab
  } else {
    syncTopTabToUrl(currentTab.value)
  }

  loadTopTabData(currentTab.value)
})

const loadGuideFloors = async () => {
  try {
    const floors = await guideUseCase.getFloors()
    guideFloors.value = floors
    activeGuideFloor.value = floors.find((floor) => floor.label === activeGuideFloor.value)?.label
      || floors.find((floor) => floor.id === 'L1')?.label
      || floors[0]?.label
      || activeGuideFloor.value
  } catch (error) {
    console.error('加载导览楼层失败:', error)
    guideFloors.value = []
  }
}

const loadExplainExhibits = async () => {
  if (explainHallItems.value.length) return
  if (explainLoadPromise) return explainLoadPromise

  explainLoading.value = true
  explainError.value = ''

  explainLoadPromise = (async () => {
    try {
      const [halls, exhibits] = await Promise.all([
        explainUseCase.listHalls(),
        explainUseCase.listExplainExhibits()
      ])
      explainHallItems.value = buildExplainHallItems(halls, exhibits)
    } catch (error) {
      console.error('加载讲解内容失败:', error)
      explainError.value = '讲解内容加载失败，请稍后重试'
      explainHallItems.value = []
    } finally {
      explainLoading.value = false
      explainLoadPromise = null
    }
  })()

  return explainLoadPromise
}

const normalizeExplainKeyword = (value?: string) => (value || '').trim().toLowerCase()

const buildExplainHallItems = (
  halls: Awaited<ReturnType<typeof explainUseCase.listHalls>>,
  exhibits: MuseumExhibit[]
): ExplainHallSelectItem[] => {
  const countsByHallId = new Map<string, number>()
  const countsByHallName = new Map<string, number>()
  const keywordsByHallId = new Map<string, Set<string>>()
  const keywordsByHallName = new Map<string, Set<string>>()

  exhibits.forEach((exhibit) => {
    const keywordParts = [
      exhibit.name,
      exhibit.hallName,
      exhibit.floorLabel,
      exhibit.description,
      exhibit.guideTitle,
      exhibit.guideText,
      ...(exhibit.tags || [])
    ].filter(Boolean) as string[]

    if (exhibit.hallId) {
      countsByHallId.set(exhibit.hallId, (countsByHallId.get(exhibit.hallId) || 0) + 1)
      const keywords = keywordsByHallId.get(exhibit.hallId) || new Set<string>()
      keywordParts.forEach((part) => keywords.add(part))
      keywordsByHallId.set(exhibit.hallId, keywords)
    }

    if (exhibit.hallName) {
      countsByHallName.set(exhibit.hallName, (countsByHallName.get(exhibit.hallName) || 0) + 1)
      const keywords = keywordsByHallName.get(exhibit.hallName) || new Set<string>()
      keywordParts.forEach((part) => keywords.add(part))
      keywordsByHallName.set(exhibit.hallName, keywords)
    }
  })

  return halls.map((hall) => {
    const explainCount = countsByHallId.get(hall.id)
      || countsByHallName.get(hall.name)
      || hall.exhibitCount
      || 0
    const keywordParts = [
      hall.name,
      hall.floorLabel,
      hall.description,
      hall.area,
      ...Array.from(keywordsByHallId.get(hall.id) || []),
      ...Array.from(keywordsByHallName.get(hall.name) || [])
    ].filter(Boolean) as string[]

    return {
      id: hall.id,
      name: hall.name,
      floorLabel: hall.floorLabel,
      image: hall.image,
      explainCount,
      searchText: normalizeExplainKeyword(keywordParts.join(' '))
    }
  })
}

const handleGuideSearchTap = () => {
  uni.navigateTo({
    url: '/pages/search/index'
  })
}

const handleFloorChange = (floor: string) => {
  activeGuideFloor.value = floor
  indoorView.value = 'floor'
  selectedGuidePoi.value = null
  isPoiCardCollapsed.value = false
  console.log('切换楼层:', floor)
}

const handleIndoorViewChange = (view: GuideIndoorView) => {
  indoorView.value = view
  if (view !== 'floor') {
    selectedGuidePoi.value = null
    isPoiCardCollapsed.value = false
  }
}

const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' | 'floor'; trigger: string; distance: number }) => {
  console.log('自动切换视图:', event)
  indoorView.value = event.to
  if (event.to !== 'floor') {
    selectedGuidePoi.value = null
    isPoiCardCollapsed.value = false
  }
}

const handleGuidePoiClick = (poi: GuideRenderPoi) => {
  selectedGuidePoi.value = poi
  isPoiCardCollapsed.value = false
  indoorView.value = 'floor'
  showGuideMoreMenu.value = false
  isSimulatingRoute.value = false
  const matchedFloor = guideFloors.value.find((floor) => floor.id === poi.floorId)
  activeGuideFloor.value = matchedFloor?.label || poi.floorId
}

const handleGuideSelectionClear = () => {
  selectedGuidePoi.value = null
  isPoiCardCollapsed.value = false
}

const collapsePoiCard = () => {
  isPoiCardCollapsed.value = true
}

const expandPoiCard = () => {
  isPoiCardCollapsed.value = false
}

const togglePoiCardCollapsed = () => {
  isPoiCardCollapsed.value = !isPoiCardCollapsed.value
}

const findRouteOptionForPoi = async (poi: GuideRenderPoi) => {
  if (!routePointOptions.value.length) {
    await loadRouteTargets()
  }

  return routePointOptions.value.find((option) => option.poiId === poi.id) || null
}

const setSelectedPoiAsRoutePoint = async (mode: 'start' | 'end') => {
  if (!selectedGuidePoi.value) return

  const option = await findRouteOptionForPoi(selectedGuidePoi.value)
  if (!option) {
    uni.showToast({
      title: '该点位暂无线路数据，可先查看三维位置',
      icon: 'none'
    })
    return
  }

  if (mode === 'start') {
    routeStartPoint.value = option
  } else {
    routeEndPoint.value = option
  }

  clearRoutePreviewState()
  openRoutePlanner()
}

const handlePreviewSelectedPoi = () => {
  if (!selectedGuidePoi.value) return
  indoorView.value = 'floor'
  showRoutePlanner.value = false
  showGuideMoreMenu.value = false
  isSimulatingRoute.value = false
}

const handleSetSelectedPoiAsStart = () => {
  void setSelectedPoiAsRoutePoint('start')
}

const handleSetSelectedPoiAsEnd = () => {
  void setSelectedPoiAsRoutePoint('end')
}

const handleSelectedPoiRelated = () => {
  if (!selectedGuidePoi.value) return
  uni.navigateTo({
    url: `/pages/search/index?keyword=${encodeURIComponent(selectedGuidePoi.value.name)}`
  })
}

const handleGuideToolClick = (tool: string) => {
  if (tool === '清除') {
    selectedGuidePoi.value = null
  } else if (tool === '清路线') {
    clearRoutePreviewState()
    isSimulatingRoute.value = false
  }
}

const loadRouteTargets = async (keyword = '') => {
  routeTargetsLoading.value = true
  routeTargetsError.value = ''

  try {
    const targets = keyword
      ? await guideRouteUseCase.searchTargets(keyword)
      : await guideRouteUseCase.listTargets()
    const options = targets.map(toRoutePointOption)
    routePointOptions.value = options
  } catch (error) {
    console.error('加载路线目标失败:', error)
    routeTargetsError.value = error instanceof Error ? error.message : '路线目标加载失败'
    routePointOptions.value = []
  } finally {
    routeTargetsLoading.value = false
  }
}

const refreshRouteReadinessState = async () => {
  try {
    const readiness = await guideRouteUseCase.getRouteReadiness()
    routePreviewUnavailableMessage.value = readiness.ready
      ? ''
      : readiness.message || '当前暂不支持正式导航，可查看位置预览和路线示意'
  } catch (error) {
    console.warn('读取路线能力状态失败:', error)
    routePreviewUnavailableMessage.value = '当前暂不支持正式导航，可查看位置预览和路线示意'
  }
}

const openRoutePlanner = () => {
  showRoutePlanner.value = true
  is3DMode.value = true
  indoorView.value = indoorView.value === 'overview' ? 'floor' : indoorView.value
  guideOutdoorState.value = 'home'
  showGuideMoreMenu.value = false
  isPoiCardCollapsed.value = false
  isSimulatingRoute.value = false
  if (!routePointOptions.value.length) {
    void loadRouteTargets()
  }
  void refreshRouteReadinessState()
}

const clearRoutePreviewState = () => {
  activeRoutePreview.value = null
  routePlanError.value = ''
  isSimulatingRoute.value = false
}

const handleRouteClear = () => {
  routeStartPoint.value = null
  routeEndPoint.value = null
  showRoutePlanner.value = false
  clearRoutePreviewState()
}

const handleRouteStartPointChange = (point: RoutePointOption | null) => {
  routeStartPoint.value = point
  clearRoutePreviewState()
}

const handleRouteEndPointChange = (point: RoutePointOption | null) => {
  routeEndPoint.value = point
  clearRoutePreviewState()
}

const handleRouteSwap = () => {
  const nextStart = routeEndPoint.value
  routeEndPoint.value = routeStartPoint.value
  routeStartPoint.value = nextStart
  clearRoutePreviewState()
}

const handleRoutePickerOpen = () => {
  if (!routePointOptions.value.length) {
    void loadRouteTargets()
  }
}

const handleRoutePickerSearch = ({ keyword }: { mode: 'start' | 'end'; keyword: string }) => {
  void loadRouteTargets(keyword)
}

const focusRouteFloor = (route: GuideRouteResult) => {
  const floor = guideFloors.value.find((item) => item.id === route.start.floorId)
  const routeFloorCount = new Set(route.floorSegments.map((segment) => segment.floorId)).size
  activeGuideFloor.value = floor?.label || route.start.floorLabel
  indoorView.value = routeFloorCount > 1 ? 'multi' : 'floor'
}

const handleRouteView = async ({ startPoint, endPoint }: { startPoint: RoutePointOption; endPoint: RoutePointOption }) => {
  routePlanning.value = true
  routePlanError.value = ''
  isSimulatingRoute.value = false

  try {
    await refreshRouteReadinessState()
    if (routePreviewUnavailableMessage.value) {
      activeRoutePreview.value = null
      routePlanError.value = routePreviewUnavailableMessage.value
      return
    }

    const result = await guideRouteUseCase.planRoute({
      startPoiId: startPoint.poiId,
      endPoiId: endPoint.poiId
    })

    if (!result.route) {
      activeRoutePreview.value = null
      routePlanError.value = result.error || '路线预览生成失败'
      return
    }

    activeRoutePreview.value = result.route
    focusRouteFloor(result.route)
    showRoutePlanner.value = true
  } catch (error) {
    console.error('路线预览生成失败:', error)
    activeRoutePreview.value = null
    routePlanError.value = error instanceof Error ? error.message : '路线预览生成失败'
  } finally {
    routePlanning.value = false
  }
}

const handleRouteSimulate = () => {
  if (!activeRoutePreview.value) return
  showRoutePlanner.value = false
  selectedGuidePoi.value = null
  isPoiCardCollapsed.value = false
  showGuideMoreMenu.value = false
  isSimulatingRoute.value = true
}

// 进入 3D 室内模式
const handleEnter3DMode = () => {
  console.log('进入 3D 室内模式')
  indoorView.value = 'floor'
  is3DMode.value = true
  guideOutdoorState.value = 'home'
  showGuideMoreMenu.value = false
  if (!routePointOptions.value.length) {
    void loadRouteTargets()
  }
}

const handleModeChange = (mode: '2d' | '3d') => {
  is3DMode.value = mode === '3d'

  if (mode === '2d') {
    indoorView.value = 'overview'
    guideOutdoorState.value = 'home'
    selectedGuidePoi.value = null
    isPoiCardCollapsed.value = false
    showRoutePlanner.value = false
    showGuideMoreMenu.value = false
    isSimulatingRoute.value = false
    clearRoutePreviewState()
  } else {
    indoorView.value = 'floor'
    if (!routePointOptions.value.length) {
      void loadRouteTargets()
    }
  }
}

const guideStatusLabel = computed(() => {
  if (is3DMode.value) {
    if (indoorView.value === 'overview') return '建筑外观'
    if (indoorView.value === 'multi') return '室内多层'
    return '室内单层'
  }

  if (guideOutdoorState.value === 'entrance') {
    return '推荐入口'
  }

  return '室外参考'
})

const handleSwitchEntrance = () => {
  guideOutdoorState.value = 'home'
}

const handleGuideMoreClick = () => {
  showGuideMoreMenu.value = !showGuideMoreMenu.value
  selectedGuidePoi.value = null
}

const handleMoreRouteGuide = () => {
  openRoutePlanner()
}

const openOutdoorReference = (state: 'home' | 'entrance') => {
  is3DMode.value = false
  guideOutdoorState.value = state
  indoorView.value = 'overview'
  selectedGuidePoi.value = null
  isPoiCardCollapsed.value = false
  showRoutePlanner.value = false
  showGuideMoreMenu.value = false
  clearRoutePreviewState()
}

const handleMoreOutdoorReference = () => {
  openOutdoorReference('home')
}

const handleMoreRecommendedEntrance = () => {
  openOutdoorReference('entrance')
}

const handleMoreToggleLayer = () => {
  is3DMode.value = true
  guideOutdoorState.value = 'home'
  indoorView.value = indoorLayerMode.value === 'multi' ? 'floor' : 'multi'
  showGuideMoreMenu.value = false
}

const guideShellMode = computed(() => (is3DMode.value ? '3d' : '2d'))
const guideMapType = computed(() => (is3DMode.value ? 'indoor' : 'outdoor'))
const guideOutdoorVariant = computed(() => (
  guideOutdoorState.value === 'entrance' ? 'entrance' : 'home'
))
const guideSearchText = computed(() => {
  return '请输入地点进行搜索'
})

// 讲解页面处理
const handleExplainHallClick = (hallId: string) => {
  uni.navigateTo({
    url: `/pages/hall/detail?id=${encodeURIComponent(hallId)}&tab=explain`
  })
}

const handleExplainBack = () => {
  currentTab.value = 'guide'
  syncTopTabToUrl('guide')
  loadTopTabData('guide')
  showGuideMoreMenu.value = false
  showRoutePlanner.value = false
  isSimulatingRoute.value = false
  isPoiCardCollapsed.value = false
}
</script>

<style scoped lang="scss">
.explain-page {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #f6f7f4;
  z-index: 10;
}

.guide-poi-card,
.guide-more-popover {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 30px);
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid #e5e6de;
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(36, 49, 42, 0.12);
  z-index: 1003;
}

.guide-poi-card {
  padding: 10px 14px 14px;
}

.guide-poi-card.collapsed {
  padding: 8px 10px 10px;
}

.poi-card-handle,
.more-popover-handle {
  width: 38px;
  height: 4px;
  margin: 0 auto 10px;
  background: #d8dbd2;
  border-radius: 999px;
}

.poi-card-collapsed {
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.poi-card-mini-action {
  flex: 0 0 auto;
  height: 30px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #151713;
  border-radius: 8px;
}

.poi-card-mini-text {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: var(--museum-accent);
}

.poi-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.poi-card-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.poi-card-title {
  font-size: 17px;
  line-height: 24px;
  font-weight: 700;
  color: #151713;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.poi-card-subtitle {
  font-size: 12px;
  line-height: 17px;
  color: #696962;
}

.poi-card-header-actions {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}

.poi-card-soft-action {
  height: 28px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border: 1px solid #e4e5df;
  border-radius: 8px;
  background: #f5f7f2;
}

.poi-card-soft-text {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: #545861;
}

.poi-card-close {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #f5f7f2;
}

.poi-card-close-text {
  font-size: 18px;
  line-height: 20px;
  color: #545861;
}

.poi-card-actions {
  margin-top: 12px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.poi-action {
  min-width: 0;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  box-sizing: border-box;
  background: #f6f7f4;
  border: 1px solid #e5e6de;
  border-radius: 8px;
}

.poi-action.primary {
  background: #151713;
  border-color: #151713;
}

.poi-action-text {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: #151713;
  text-align: center;
}

.poi-action.primary .poi-action-text {
  color: var(--museum-accent);
}

.guide-more-popover {
  padding: 10px 12px 12px;
}

.more-popover-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.more-action {
  min-height: 64px;
  padding: 10px 8px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  box-sizing: border-box;
  background: #f6f7f4;
  border: 1px solid #e5e6de;
  border-radius: 8px;
}

.more-action-title {
  font-size: 14px;
  line-height: 20px;
  font-weight: 700;
  color: #151713;
  text-align: center;
}

.more-action-desc {
  font-size: 11px;
  line-height: 15px;
  color: #696962;
  text-align: center;
}

.route-sim-top-pill {
  position: absolute;
  top: 126px;
  left: 50%;
  max-width: calc(100% - 40px);
  height: 34px;
  padding: 0 14px;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  background: rgba(21, 23, 19, 0.86);
  border-radius: 8px;
  transform: translateX(-50%);
  z-index: 1003;
}

.route-sim-top-text {
  min-width: 0;
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: #ffffff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.route-sim-step-bar {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 30px);
  min-height: 54px;
  padding: 10px 10px 10px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid #e5e6de;
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(36, 49, 42, 0.12);
  z-index: 1003;
}

.route-sim-step-text {
  min-width: 0;
  flex: 1;
  font-size: 14px;
  line-height: 20px;
  font-weight: 700;
  color: #151713;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.route-sim-expand {
  height: 34px;
  padding: 0 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  background: #000000;
  border-radius: 8px;
}

.route-sim-expand-text {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: var(--museum-accent);
}

.entrance-tip {
  position: absolute;
  top: 126px;
  left: 30px;
  width: 168px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid #e5e6de;
  border-radius: 12px;
  z-index: 45;
}

.entrance-tip-text {
  font-size: 14px;
  line-height: 19px;
  font-weight: 500;
  color: #1f2329;
}

.home-shortcuts-card {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 134px);
  height: 84px;
  padding: 16px 10px 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid #ffffff;
  border-radius: 16px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
  z-index: 45;
}

.home-shortcut-item {
  width: 62px;
  height: 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
}

.home-shortcut-icon {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  background: #ffffff;
  border: 1px solid #d9dcd2;
  border-radius: 10px;
}

.home-shortcut-abbr {
  font-size: 15px;
  line-height: 20px;
  font-weight: 700;
  color: #1f2329;
}

.home-shortcut-label {
  font-size: 12px;
  line-height: 16px;
  color: #1f2329;
  text-align: center;
}

.home-shortcut-item.active {
  .home-shortcut-icon {
    background: #000000;
    border-color: #000000;
  }

  .home-shortcut-abbr {
    color: var(--museum-accent);
  }
}

.search-bar-wrapper {
  position: absolute;
  top: 44px;
  left: 0;
  right: 0;
  z-index: 1003;
}

.guide-mode-row {
  position: absolute;
  top: 104px;
  left: 16px;
  right: 16px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 1003;
}

.mode-switch {
  width: 220px;
  height: 34px;
  display: flex;
  align-items: center;
  padding: 4px;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(230, 230, 230, 0.5);
  border-radius: 17px;
}

.mode-option {
  width: 106px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 13px;
}

.mode-option.active {
  background: #000000;
}

.mode-label {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: #1f2329;
}

.mode-option.active .mode-label {
  color: var(--museum-accent);
}

.mode-status-chip {
  width: 111px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.42);
  border-radius: 14px;
}

.mode-status-text {
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  color: #696962;
}

.area-selector-wrapper {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 134px);
  z-index: 1003;
}

.guide-task-card {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 30px);
  height: 94px;
  padding: 16px 16px 10px;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  background: #ffffff;
  border: 1px solid #ffffff;
  border-radius: 16px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
  z-index: 1003;
}

.guide-task-card.has-subtitle {
  height: auto;
  min-height: 128px;
}

.task-title {
  flex-shrink: 0;
  font-size: 17px;
  line-height: 24px;
  font-weight: 700;
  color: #000000;
}

.task-subtitle {
  margin-top: 4px;
  min-height: 20px;
  font-size: 12px;
  line-height: 18px;
  color: #696962;
}

.task-primary-btn {
  flex-shrink: 0;
  margin-top: 4px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000000;
  border-radius: 8px;
}

.task-primary-text {
  font-size: 14px;
  line-height: 19px;
  font-weight: 500;
  color: var(--museum-accent);
}

.entrance-bottom-card {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(env(safe-area-inset-bottom) + 34px);
  height: 158px;
  padding: 18px 16px 14px;
  box-sizing: border-box;
  background: #ffffff;
  border: 1px solid #ffffff;
  border-radius: 16px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
  z-index: 45;
}

.entrance-card-title {
  display: block;
  font-size: 17px;
  line-height: 24px;
  font-weight: 700;
  color: #000000;
}

.entrance-card-desc {
  display: block;
  width: 305px;
  margin-top: 10px;
  font-size: 13px;
  line-height: 18px;
  color: #6b7178;
}

.entrance-actions {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 14px;
  display: flex;
  gap: 12px;
}

.entrance-btn {
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 8px;
}

.entrance-btn.secondary {
  width: 126px;
  background: #ffffff;
  border: 1px solid #000000;
}

.entrance-btn.primary {
  width: 181px;
  background: #000000;
  border: 1px solid #000000;
}

.entrance-btn-text {
  font-size: 14px;
  line-height: 19px;
  font-weight: 500;
  color: #000000;
}

.entrance-btn.primary .entrance-btn-text {
  color: var(--museum-accent);
}

</style>
