<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted } from 'vue'
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
import MiniLocationMap from '@/components/cloud/MiniLocationMap.vue'
import QuickUploadModal from '@/components/cloud/QuickUploadModal.vue'
import { supabase } from '@/lib/supabase'
import { loadAMap } from '@/lib/amap'
import { NIcon } from 'naive-ui'
import { Adjustments, Calendar, CloudUpload, Refresh, Map, Satellite, X } from '@vicons/tabler'

interface CloudMarkerData {
  id: string
  latitude: number
  longitude: number
  imageUrl: string
  thumbnailUrl: string | null
  locationName: string | null
  description: string | null
  cloudTypeName: string
  rarity: 'common' | 'uncommon' | 'rare'
  username: string
  capturedAt: string
  createdAt: string
}

const mapEl = ref<HTMLDivElement>()
const previewCloud = ref<CloudMarkerData | null>(null)
const satelliteOn = ref(false)
const statusText = ref('加载中...')
const quickUploadOpen = ref(false)
const mapMode = ref<'realtime' | 'archive'>('realtime')
const timelineControlsOpen = ref(false)
const archivePanelOpen = ref(false)
const archiveKind = ref<'day' | 'month'>('day')
const archiveDay = ref(formatDateInput(new Date()))
const archiveMonth = ref(formatMonthInput(new Date()))
const currentMinuteOfDay = ref(getMinuteOfDay(new Date()))
const selectedMinuteOfDay = ref(currentMinuteOfDay.value)
const sliderDragging = ref(false)

const VISIBLE_WINDOW_MS = 2 * 60 * 60 * 1000
const MIN_MARKER_OPACITY = 0.3
const HIDE_HEADER_EVENT = 'opencloud:hide-header'

let AMapLib: typeof AMap | null = null
let mapInst: AMap.Map | null = null
let satLayer: AMap.TileLayer | null = null
let roadLayer: AMap.TileLayer | null = null
let mks: AMap.Marker[] = []
let hoverIW: AMap.InfoWindow | null = null
let allClouds: CloudMarkerData[] = []
let redrawTimer: number | null = null

const rarityColors: Record<string, string> = {
  common: '#cbd5e1',
  uncommon: '#60a5fa',
  rare: '#c084fc',
}

const rarityMeta = {
  common: { label: '常见', chip: 'bg-sky-100 text-sky-700 border-sky-200' },
  uncommon: { label: '少见', chip: 'bg-amber-100 text-amber-700 border-amber-200' },
  rare: { label: '罕见', chip: 'bg-rose-100 text-rose-700 border-rose-200' },
} as const

const selectedTimeLabel = computed(() => formatMinuteOfDay(selectedMinuteOfDay.value))
const currentMinutePercent = computed(() => Math.min(100, Math.max(0, currentMinuteOfDay.value / 1439 * 100)))
const selectedMinutePercent = computed(() => Math.min(100, Math.max(0, selectedMinuteOfDay.value / 1439 * 100)))
const archiveTitle = computed(() => {
  if (mapMode.value === 'realtime') return '实时'
  return archiveKind.value === 'day' ? archiveDay.value : archiveMonth.value
})

function getMinuteOfDay(date: Date) {
  return date.getHours() * 60 + date.getMinutes()
}

function formatMinuteOfDay(minute: number) {
  const hours = Math.floor(minute / 60)
  const minutes = minute % 60
  return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`
}

function formatDateInput(date: Date) {
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  return `${date.getFullYear()}-${month}-${day}`
}

function formatMonthInput(date: Date) {
  const month = String(date.getMonth() + 1).padStart(2, '0')
  return `${date.getFullYear()}-${month}`
}

function getLocalDateRange(dateValue: string) {
  const [year, month, day] = dateValue.split('-').map(Number)
  const start = new Date(year, month - 1, day, 0, 0, 0, 0)
  const end = new Date(year, month - 1, day + 1, 0, 0, 0, 0)
  return { start, end }
}

function getLocalMonthRange(monthValue: string) {
  const [year, month] = monthValue.split('-').map(Number)
  const start = new Date(year, month - 1, 1, 0, 0, 0, 0)
  const end = new Date(year, month, 1, 0, 0, 0, 0)
  return { start, end }
}

function getSelectedRealtimeDate() {
  const { start } = getLocalDateRange(formatDateInput(new Date()))
  return new Date(start.getTime() + selectedMinuteOfDay.value * 60 * 1000)
}

function syncCurrentMinute() {
  currentMinuteOfDay.value = getMinuteOfDay(new Date())
  if (selectedMinuteOfDay.value > currentMinuteOfDay.value) {
    selectedMinuteOfDay.value = currentMinuteOfDay.value
  }
}

function timeAgo(iso: string): string {
  const diff = Date.now() - new Date(iso).getTime()
  const m = Math.floor(diff / 60000)
  if (m < 1) return '刚刚'
  if (m < 60) return `${m} 分钟前`
  const h = Math.floor(m / 60)
  if (h < 24) return `${h} 小时前`
  const d = Math.floor(h / 24)
  if (d < 30) return `${d} 天前`
  return new Date(iso).toLocaleDateString('zh-CN')
}

function formatDateTime(iso: string | null) {
  if (!iso) return '未知时间'
  return new Date(iso).toLocaleString('zh-CN', {
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  })
}

function formatCoordinate(value: number | null) {
  return value === null ? '未记录' : value.toFixed(2)
}

function getCloudAgeMs(cloud: CloudMarkerData) {
  const capturedTime = new Date(cloud.capturedAt).getTime()
  const fallbackTime = new Date(cloud.createdAt).getTime()
  const targetTime = Number.isNaN(capturedTime) ? fallbackTime : capturedTime
  return getSelectedRealtimeDate().getTime() - targetTime
}

function getMarkerOpacity(cloud: CloudMarkerData) {
  if (mapMode.value === 'archive') return 1

  const ageMs = getCloudAgeMs(cloud)
  if (ageMs < 0) return null
  if (ageMs >= VISIBLE_WINDOW_MS) return null

  const progress = ageMs / VISIBLE_WINDOW_MS
  return 1 - progress * (1 - MIN_MARKER_OPACITY)
}

function bubbleHtml(cloud: CloudMarkerData): string {
  const border = rarityColors[cloud.rarity] || rarityColors.common
  const bubbleImage = cloud.thumbnailUrl || cloud.imageUrl
  const opacity = getMarkerOpacity(cloud) ?? MIN_MARKER_OPACITY
  return `<div style="opacity:${opacity};width:46px;height:46px;cursor:pointer;line-height:0">
    <div style="box-sizing:border-box;width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,#ffffff 0%,#e8ecf1 100%);border:3px solid ${border};box-shadow:0 4px 16px rgba(0,0,0,0.3),inset 0 2px 4px rgba(255,255,255,0.6),0 0 0 2px rgba(0,0,0,0.06);overflow:hidden">
      <img src="${bubbleImage}" style="display:block;width:100%;height:100%;object-fit:cover" />
    </div>
  </div>`
}

function hoverCardHtml(cloud: CloudMarkerData): string {
  const border = rarityColors[cloud.rarity] || rarityColors.common
  const cardImage = cloud.thumbnailUrl || cloud.imageUrl
  const label = cloud.rarity === 'common' ? '常见' : cloud.rarity === 'uncommon' ? '少见' : '罕见'
  return `<div style="background:#fff;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.18);overflow:hidden;width:200px;font-family:system-ui,-apple-system,sans-serif">
    <img src="${cardImage}" style="display:block;width:100%;height:110px;object-fit:cover" />
    <div style="padding:8px 10px">
      <div style="display:flex;align-items:center;gap:6px">
        <span style="font-size:13px;font-weight:600;color:#1f2937">${cloud.cloudTypeName}</span>
        <span style="font-size:10px;padding:1px 5px;border-radius:3px;color:${border};background:${border}22">${label}</span>
      </div>
      <div style="font-size:11px;color:#9ca3af;margin-top:3px">📷 ${cloud.username} · ${timeAgo(cloud.capturedAt)}</div>
    </div>
  </div>`
}

function showHoverCard(cloud: CloudMarkerData, pos: [number, number]) {
  if (!AMapLib || !mapInst) return
  hoverIW?.close()
  const iw = new AMapLib.InfoWindow({
    content: hoverCardHtml(cloud),
    offset: new AMapLib.Pixel(-100, -60),
    isCustom: true,
  } as AMap.InfoWindowOptions)
  iw.open(mapInst, pos)
  hoverIW = iw
}

function hideHoverCard() {
  hoverIW?.close()
  hoverIW = null
}

function hideHeader() {
  window.dispatchEvent(new CustomEvent(HIDE_HEADER_EVENT))
}

function toCloudMarker(row: Record<string, unknown>): CloudMarkerData {
  const ct = row.cloud_types as Record<string, unknown> | null
  const pf = row.profiles as Record<string, unknown> | null
  return {
    id: row.id as string,
    latitude: row.latitude as number,
    longitude: row.longitude as number,
    imageUrl: row.image_url as string,
    thumbnailUrl: (row.thumbnail_url as string | null) ?? null,
    locationName: (row.location_name as string | null) ?? null,
    description: (row.description as string | null) ?? null,
    cloudTypeName: (ct?.name as string) || (row.custom_cloud_type as string) || '未知',
    rarity: (ct?.rarity as 'common' | 'uncommon' | 'rare') || 'common',
    username: (pf?.username as string) || '匿名',
    capturedAt: (row.captured_at as string) || (row.created_at as string),
    createdAt: row.created_at as string,
  }
}

async function fetchCloudsByRange(field: 'captured_at' | 'created_at', start: Date, end: Date): Promise<CloudMarkerData[]> {
  const { data, error } = await supabase
    .from('clouds')
    .select('id,image_url,thumbnail_url,latitude,longitude,location_name,description,captured_at,created_at,custom_cloud_type,cloud_types(name,rarity),profiles(username)')
    .eq('status', 'approved')
    .eq('is_hidden', false)
    .not('latitude', 'is', null)
    .not('longitude', 'is', null)
    .gte(field, start.toISOString())
    .lt(field, end.toISOString())
    .order(field, { ascending: true })
    .limit(1000)

  if (error) {
    statusText.value = `查询失败: ${error.message}`
    return []
  }

  return ((data || []) as Array<Record<string, unknown>>).map(toCloudMarker)
}

async function loadRealtimeClouds() {
  const { start, end } = getLocalDateRange(formatDateInput(new Date()))
  return fetchCloudsByRange('captured_at', start, end)
}

async function loadArchiveClouds() {
  const range = archiveKind.value === 'day'
    ? getLocalDateRange(archiveDay.value)
    : getLocalMonthRange(archiveMonth.value)
  return fetchCloudsByRange('captured_at', range.start, range.end)
}

function getVisibleClouds(clouds: CloudMarkerData[]) {
  if (mapMode.value === 'archive') return clouds

  return clouds.filter(cloud => {
    const opacity = getMarkerOpacity(cloud)
    return opacity !== null
  })
}

function clearMarkers() {
  hideHoverCard()
  for (const m of mks) m.setMap(null)
  mks = []
}

function drawMarkers(clouds: CloudMarkerData[]) {
  if (!AMapLib) return
  clearMarkers()
  const visibleClouds = getVisibleClouds(clouds)
  statusText.value = mapMode.value === 'realtime'
    ? `${visibleClouds.length} 朵云（${selectedTimeLabel.value} 前 2 小时）`
    : `${visibleClouds.length} 朵云（${archiveTitle.value} 拍摄）`

  if (previewCloud.value && !visibleClouds.some(item => item.id === previewCloud.value?.id)) {
    previewCloud.value = null
  }

  for (const c of visibleClouds) {
    const m = new AMapLib.Marker({
      position: [c.longitude, c.latitude],
      content: bubbleHtml(c),
      offset: new AMapLib.Pixel(-23, -23),
      zIndex: 200,
    } as AMap.MarkerOptions)
    m.on('mouseover', () => showHoverCard(c, [c.longitude, c.latitude]))
    m.on('mouseout', () => hideHoverCard())
    m.on('click', () => {
      previewCloud.value = c
    })
    m.setMap(mapInst!)
    mks.push(m)
  }
}

async function refresh() {
  if (mapMode.value === 'realtime') {
    syncCurrentMinute()
  }
  statusText.value = '加载中...'
  allClouds = mapMode.value === 'archive'
    ? await loadArchiveClouds()
    : await loadRealtimeClouds()
  drawMarkers(allClouds)
}

function startMarkerDecayTimer() {
  if (redrawTimer !== null) window.clearInterval(redrawTimer)
  redrawTimer = window.setInterval(() => {
    if (mapMode.value === 'realtime') {
      syncCurrentMinute()
    }
    drawMarkers(allClouds)
  }, 60 * 1000)
}

function toggleSat() {
  if (!AMapLib || !mapInst) return
  if (satelliteOn.value) {
    if (satLayer) mapInst.remove(satLayer as unknown as Parameters<typeof mapInst.remove>[0])
    if (roadLayer) mapInst.remove(roadLayer as unknown as Parameters<typeof mapInst.remove>[0])
    satelliteOn.value = false
  } else {
    satLayer = satLayer || new AMapLib.TileLayer.Satellite()
    roadLayer = roadLayer || new AMapLib.TileLayer.RoadNet()
    mapInst.add(satLayer as unknown as Parameters<typeof mapInst.add>[0])
    mapInst.add(roadLayer as unknown as Parameters<typeof mapInst.add>[0])
    satelliteOn.value = true
  }
}

function openQuickUpload() {
  quickUploadOpen.value = true
}

function closeQuickUpload() {
  quickUploadOpen.value = false
}

async function handleQuickUploaded() {
  await refresh()
}

function handleTimeSliderInput(event: Event) {
  syncCurrentMinute()
  selectedMinuteOfDay.value = Math.min(Number((event.target as HTMLInputElement).value), currentMinuteOfDay.value)
  mapMode.value = 'realtime'
  archivePanelOpen.value = false
  drawMarkers(allClouds)
}

function handleSliderPointerDown() {
  sliderDragging.value = true
}

function handleSliderPointerUp() {
  sliderDragging.value = false
}

async function toggleTimelineControls() {
  timelineControlsOpen.value = !timelineControlsOpen.value
  if (timelineControlsOpen.value) {
    syncCurrentMinute()
    selectedMinuteOfDay.value = currentMinuteOfDay.value
  } else {
    archivePanelOpen.value = false
    sliderDragging.value = false
    if (mapMode.value === 'archive') {
      await returnRealtime()
    }
  }
}

async function applyArchiveFilter() {
  mapMode.value = 'archive'
  archivePanelOpen.value = false
  await refresh()
}

async function returnRealtime() {
  mapMode.value = 'realtime'
  archivePanelOpen.value = false
  await refresh()
}

onMounted(async () => {
  try {
    AMapLib = await loadAMap()
    mapInst = new AMapLib.Map(mapEl.value!, {
      viewMode: '3D',
      pitch: 0,
      rotation: 0,
      zoom: 5,
      center: [104.07, 30.67],
      mapStyle: 'amap://styles/normal',
      features: ['bg', 'road', 'building', 'point'],
      resizeEnable: true,
    } as AMap.MapOptions)

    mapInst.addControl(new AMapLib.Scale())
    mapInst.addControl(new AMapLib.ToolBar({ position: 'LT' } as Record<string, unknown>))
    mapInst.addControl(new AMapLib.ControlBar({ position: { right: '10px', top: '80px' } } as Record<string, unknown>))
    mapInst.on('click', () => { previewCloud.value = null; hideHoverCard() })
    mapInst.on('zoomstart', hideHeader)
    mapInst.on('movestart', hideHeader)
    mapInst.on('dragstart', hideHeader)
    await refresh()
    startMarkerDecayTimer()
  } catch (e) {
    statusText.value = `加载失败: ${e instanceof Error ? e.message : String(e)}`
  }
})

onUnmounted(() => {
  if (redrawTimer !== null) {
    window.clearInterval(redrawTimer)
    redrawTimer = null
  }
  mapInst?.destroy()
  mapInst = null
  AMapLib = null
})
</script>

<template>
  <div class="relative h-[100dvh] min-h-screen">
    <div ref="mapEl" class="w-full h-full"></div>

    <div class="absolute bottom-6 right-4 flex flex-col items-end gap-2 z-20">
      <button
        type="button"
        class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50"
        title="快速上传图片"
        aria-label="快速上传图片"
        @click="openQuickUpload"
      >
        <NIcon size="20" style="display: inline-flex; vertical-align: middle;">
          <CloudUpload />
        </NIcon>
      </button>
      <button @click="refresh" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" title="刷新">
        <span class="text-lg">
          <NIcon>
            <Refresh/>
          </NIcon>
        </span>
      </button>
      <button @click="toggleSat" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" :title="satelliteOn ? '切换普通视图' : '切换卫星视图'">
        <NIcon size="20" style="display: inline-flex; vertical-align: middle;">
          <Map v-if="satelliteOn" />
          <Satellite v-else />
        </NIcon>
      </button>
      <button
        type="button"
        class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50"
        :title="timelineControlsOpen ? '收起时间控件' : '展开时间控件'"
        :aria-label="timelineControlsOpen ? '收起时间控件' : '展开时间控件'"
        @click="toggleTimelineControls"
      >
        <NIcon size="20" style="display: inline-flex; vertical-align: middle;">
          <X v-if="timelineControlsOpen" />
          <Adjustments v-else />
        </NIcon>
      </button>
    </div>

    <div class="absolute bottom-6 right-16 z-10 flex justify-end pointer-events-none">
      <div class="pointer-events-auto flex items-center justify-end gap-3">
        <Transition name="map-timeline-controls">
          <div v-if="timelineControlsOpen" class="flex items-center gap-3">
        <div class="relative w-[min(42vw,420px)] min-w-[220px]">
          <div
            v-if="sliderDragging"
            class="absolute bottom-8 border border-slate-200 bg-white/95 px-3 py-1.5 text-xs font-medium text-slate-800 shadow-md backdrop-blur"
            :style="{ left: `${selectedMinutePercent}%`, transform: 'translateX(-50%)' }"
          >
            {{ selectedTimeLabel }}
          </div>
          <div class="relative">
            <input
              type="range"
              min="0"
              max="1439"
              step="10"
              :value="selectedMinuteOfDay"
              class="map-time-slider block w-full accent-sky-500"
              :disabled="mapMode === 'archive'"
              @pointerdown="handleSliderPointerDown"
              @pointerup="handleSliderPointerUp"
              @pointercancel="handleSliderPointerUp"
              @blur="handleSliderPointerUp"
              @input="handleTimeSliderInput"
            />
            <div
              v-if="mapMode === 'realtime'"
              class="absolute bottom-0 top-0 z-10"
              :style="{ left: `${currentMinutePercent}%`, right: '0' }"
              @pointerdown.prevent
              @click.prevent
            ></div>
          </div>
        </div>

          <div class="relative">
          <button
            type="button"
            class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50"
            title="按日期或月份查看"
            aria-label="按日期或月份查看"
            @click="archivePanelOpen = !archivePanelOpen"
          >
            <NIcon size="20" style="display: inline-flex; vertical-align: middle;">
              <Calendar />
            </NIcon>
          </button>

          <div
            v-if="archivePanelOpen"
            class="absolute bottom-16 right-0 w-72 border border-slate-200 bg-white p-4 shadow-[8px_8px_0_0_rgba(15,23,42,0.08)]"
          >
            <div class="flex items-center justify-between gap-2">
              <p class="text-sm font-semibold text-slate-900">历史位置</p>
              <button type="button" class="text-xs font-medium text-sky-700 hover:text-sky-900" @click="returnRealtime">
                返回实时
              </button>
            </div>
            <div class="mt-4 grid grid-cols-2 gap-2">
              <button
                type="button"
                class="border px-3 py-2 text-sm font-medium transition-colors"
                :class="archiveKind === 'day' ? 'border-teal-200 bg-teal-50 text-teal-800' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
                @click="archiveKind = 'day'"
              >
                某天
              </button>
              <button
                type="button"
                class="border px-3 py-2 text-sm font-medium transition-colors"
                :class="archiveKind === 'month' ? 'border-teal-200 bg-teal-50 text-teal-800' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
                @click="archiveKind = 'month'"
              >
                某月
              </button>
            </div>

            <input
              v-if="archiveKind === 'day'"
              v-model="archiveDay"
              type="date"
              class="mt-3 w-full border border-slate-300 px-3 py-2 text-sm text-slate-800 outline-none focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
            />
            <input
              v-else
              v-model="archiveMonth"
              type="month"
              class="mt-3 w-full border border-slate-300 px-3 py-2 text-sm text-slate-800 outline-none focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
            />

            <button
              type="button"
              class="mt-3 w-full border border-sky-200 bg-sky-100 px-3 py-2 text-sm font-medium text-sky-800 shadow-[4px_4px_0_0_rgba(14,165,233,0.14)] transition-colors hover:bg-sky-50"
              @click="applyArchiveFilter"
            >
              查看拍摄位置
            </button>
          </div>
          </div>
          </div>
        </Transition>
      </div>
    </div>

    <ImageDetailModal
      v-if="previewCloud"
      :open="!!previewCloud"
      :image-url="previewCloud.imageUrl"
      :thumbnail-url="previewCloud.thumbnailUrl"
      :image-alt="previewCloud.cloudTypeName"
      :title="previewCloud.cloudTypeName"
      :subtitle="`上传者：${previewCloud.username}`"
      :badge-label="rarityMeta[previewCloud.rarity].label"
      :badge-class="rarityMeta[previewCloud.rarity].chip"
      @close="previewCloud = null"
    >
      <div class="grid gap-4 sm:grid-cols-2">
        <div class="rounded-2xl bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">上传时间</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ formatDateTime(previewCloud.createdAt) }}</p>
        </div>
        <div class="rounded-2xl bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">拍摄时间</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ formatDateTime(previewCloud.capturedAt) }}</p>
        </div>
        <div class="rounded-2xl bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">位置</p>
          <p class="mt-2 text-sm font-medium text-slate-900">{{ previewCloud.locationName || '未填写位置名称' }}</p>
        </div>
        <div class="rounded-2xl bg-slate-50 p-4">
          <p class="text-xs uppercase tracking-[0.18em] text-slate-500">模糊化经纬度</p>
          <p class="mt-2 text-sm font-medium text-slate-900">
            纬度 {{ formatCoordinate(previewCloud.latitude) }} / 经度 {{ formatCoordinate(previewCloud.longitude) }}
          </p>
        </div>
      </div>

      <div class="mt-5 rounded-[28px] border border-slate-200 bg-white p-5">
        <p class="text-xs uppercase tracking-[0.18em] text-slate-500">图片说明</p>
        <p class="mt-3 text-sm leading-7 text-slate-700">
          {{ previewCloud.description || '上传者没有留下额外说明。' }}
        </p>
      </div>
      <MiniLocationMap
        :latitude="previewCloud.latitude"
        :longitude="previewCloud.longitude"
        :location-name="previewCloud.locationName"
      />
    </ImageDetailModal>

    <QuickUploadModal
      :open="quickUploadOpen"
      @close="closeQuickUpload"
      @uploaded="handleQuickUploaded"
    />
  </div>
</template>

<style scoped>
.map-archive-button.map-archive-button {
  border-radius: 9999px !important;
}

.map-time-slider:disabled {
  opacity: 0.45;
}

.map-time-slider {
  appearance: none;
  background: transparent;
  height: 18px;
}

.map-time-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 9999px;
  border: 1px solid rgba(186, 230, 253, 0.85);
  background: rgba(255, 255, 255, 0.82);
  box-shadow: 0 3px 12px rgba(15, 23, 42, 0.12);
}

.map-time-slider::-webkit-slider-thumb {
  appearance: none;
  width: 24px;
  height: 24px;
  margin-top: -10px;
  border: 2px solid rgba(255, 255, 255, 0.95);
  border-radius: 9999px;
  background-color: #e0f2fe;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%230ea5e9' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 18a4.5 4.5 0 1 1 .5-8.97A6 6 0 0 1 18.35 11.5A3.5 3.5 0 1 1 18 18Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 18px 18px;
  box-shadow: 0 2px 10px rgba(14, 165, 233, 0.34);
}

.map-time-slider::-moz-range-track {
  height: 6px;
  border-radius: 9999px;
  border: 1px solid rgba(186, 230, 253, 0.85);
  background: rgba(255, 255, 255, 0.82);
  box-shadow: 0 3px 12px rgba(15, 23, 42, 0.12);
}

.map-time-slider::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border: 2px solid rgba(255, 255, 255, 0.95);
  border-radius: 9999px;
  background-color: #e0f2fe;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%230ea5e9' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 18a4.5 4.5 0 1 1 .5-8.97A6 6 0 0 1 18.35 11.5A3.5 3.5 0 1 1 18 18Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 18px 18px;
  box-shadow: 0 2px 10px rgba(14, 165, 233, 0.34);
}

.map-timeline-controls-enter-active,
.map-timeline-controls-leave-active {
  overflow: hidden;
  transition:
    opacity 180ms ease,
    transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
    max-width 180ms cubic-bezier(0.22, 1, 0.36, 1);
}

.map-timeline-controls-enter-from,
.map-timeline-controls-leave-to {
  max-width: 0;
  opacity: 0;
  transform: translateX(-8px);
}

.map-timeline-controls-enter-to,
.map-timeline-controls-leave-from {
  max-width: 560px;
  opacity: 1;
  transform: translateX(0);
}
</style>
