<template>
  <div class="location-tooltip-wrapper" @mouseenter="showTooltip" @mouseleave="onWrapperLeave">
    <slot></slot>
    <Teleport to="body">
      <div
        v-if="visible && hasPosition"
        class="map-tooltip"
        :style="tooltipStyle"
        ref="tooltipRef"
        @mouseenter="onTooltipEnter"
        @mouseleave="onTooltipLeave"
        @wheel.prevent="onWheel"
      >
        <div class="map-tooltip-content">
          <div v-if="levelUnknown" class="map-level-unknown">
            <strong>Level unknown</strong>
            <span>
              This asset has a position ({{ props.left }}, {{ props.top }}) but no
              level, so there is no drawing to show it on. Set its level on the
              asset, or place it in the map editor.
            </span>
          </div>
          <div v-else class="map-preview" ref="mapPreview">
            <div
              class="map-transform"
              :style="transformStyle"
            >
              <img
                :src="blueprintUrl"
                alt="Shop Floor Map"
                class="map-image"
                @load="onImageLoad"
              />
              <!-- Marker dot -->
              <div
                class="marker-dot"
                :style="markerStyle"
              ></div>
            </div>
          </div>
          <div class="map-tooltip-footer">
            <span class="coordinates">{{ left }}, {{ top }}</span>
            <span class="zoom-hint">Scroll to zoom</span>
          </div>
        </div>
      </div>
    </Teleport>
  </div>
</template>

<script setup>
import { ref, computed, nextTick, watch } from 'vue'
import { currentTheme } from '../stores/theme'
import { loadMapConfig, blueprintUrlFor, dimensionsFor, hasLevel, levelName, state as mapConfig } from '../composables/mapConfig'

// Fetch this facility's blueprint + dimensions once; computeds below react
// when it loads.
loadMapConfig()

const props = defineProps({
  left: { type: Number, default: null },
  top: { type: Number, default: null },
  machineName: { type: String, default: '' },
  // Which drawing left/top are pixels of (ADR-017). A position without one
  // cannot be rendered: the same coordinates land somewhere different on every
  // level, so this shows what is missing instead of guessing the default.
  levelid: { type: Number, default: null },
})

const visible = ref(false)
const tooltipRef = ref(null)
const mapPreview = ref(null)
const tooltipPosition = ref({ x: 0, y: 0 })
const isOverTooltip = ref(false)
const zoom = ref(1)
const imageLoaded = ref(false)

const hasPosition = computed(() => {
  return props.left !== null && props.top !== null
})

// A position we cannot place: coordinates but no level, or a level this instance
// does not know. Rendering the default blueprint here would look correct and be
// wrong, so the tooltip says so instead.
const levelUnknown = computed(() => {
  return hasPosition.value && !hasLevel(props.levelid)
})

const levelLabel = computed(() => levelName(props.levelid))

const blueprintUrl = computed(() => {
  // Reading currentTheme keeps this reactive to theme changes.
  return blueprintUrlFor(currentTheme.value, props.levelid)
})

// Marker position as a percentage of THIS LEVEL's native size. Percentages of
// the wrong level's dimensions is precisely how a marker ends up plausibly
// placed and wrong.
const markerX = computed(() => {
  return (props.left / dimensionsFor(props.levelid).width) * 100
})

const markerY = computed(() => {
  return (props.top / dimensionsFor(props.levelid).height) * 100
})

// Marker style with counter-scale to maintain constant size
const markerStyle = computed(() => ({
  left: markerX.value + '%',
  top: markerY.value + '%',
  transform: `translate(-50%, -50%) scale(${1 / zoom.value})`
}))

// Transform style that centers on the marker and zooms toward it
const transformStyle = computed(() => {
  // Calculate translation to center the marker in the preview
  const translateX = 50 - markerX.value
  const translateY = 50 - markerY.value

  return {
    transform: `translate(${translateX}%, ${translateY}%) scale(${zoom.value})`,
    transformOrigin: `${markerX.value}% ${markerY.value}%`
  }
})

const tooltipStyle = computed(() => ({
  left: `${tooltipPosition.value.x}px`,
  top: `${tooltipPosition.value.y}px`
}))

function onImageLoad() {
  imageLoaded.value = true
}

function showTooltip(event) {
  if (!hasPosition.value) return

  visible.value = true
  zoom.value = 1

  const rect = event.target.getBoundingClientRect()
  tooltipPosition.value = {
    x: rect.left + rect.width / 2,
    y: rect.bottom + 10
  }

  nextTick(() => {
    adjustPosition()
  })
}

function onWrapperLeave() {
  // Small delay to allow moving to tooltip
  setTimeout(() => {
    if (!isOverTooltip.value) {
      hideTooltip()
    }
  }, 100)
}

function onTooltipEnter() {
  isOverTooltip.value = true
}

function onTooltipLeave() {
  isOverTooltip.value = false
  hideTooltip()
}

function hideTooltip() {
  visible.value = false
  zoom.value = 1
}

function onWheel(event) {
  const delta = event.deltaY > 0 ? -0.3 : 0.3
  const newZoom = Math.max(1, Math.min(8, zoom.value + delta))
  zoom.value = newZoom
}

function adjustPosition() {
  if (!tooltipRef.value) return

  const tooltip = tooltipRef.value
  const rect = tooltip.getBoundingClientRect()
  const viewportWidth = window.innerWidth
  const viewportHeight = window.innerHeight

  if (rect.right > viewportWidth - 20) {
    tooltipPosition.value.x -= (rect.right - viewportWidth + 20)
  }
  if (rect.left < 20) {
    tooltipPosition.value.x += (20 - rect.left)
  }

  if (rect.bottom > viewportHeight - 20) {
    tooltipPosition.value.y = rect.top - tooltip.offsetHeight - 20
  }
}

// Reset zoom when tooltip becomes visible
watch(visible, (newVal) => {
  if (newVal) {
    zoom.value = 1
  }
})

// Reset imageLoaded when theme changes to force reload
watch(currentTheme, () => {
  imageLoaded.value = false
})
</script>

<style scoped>
.location-tooltip-wrapper {
  display: inline;
  cursor: pointer;
}

.location-tooltip-wrapper:hover {
  color: var(--primary, #1976d2);
}

.map-level-unknown {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.75rem;
  max-width: 18rem;
  font-size: 0.8rem;
  color: var(--text-light);
}

.map-level-unknown strong {
  color: var(--warning);
}
</style>

<style>
.map-tooltip {
  position: fixed;
  z-index: 10000;
  transform: translateX(-50%);
}

.map-tooltip-content {
  background: var(--bg-card, #ffffff);
  border-radius: 8px;
  border: 1px solid var(--border, #e0e0e0);
  box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  overflow: hidden;
}

.map-preview {
  position: relative;
  /* Smaller than it was: this is a glance-and-move-on preview, and at 500px it
     covered the row it was launched from. Aspect ratio kept. */
  width: 390px;
  height: 300px;
  overflow: hidden;
  background: var(--bg, #f5f5f5);
}

.map-transform {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.15s ease-out;
}

.map-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.marker-dot {
  position: absolute;
  width: 16px;
  height: 16px;
  background: #ff0000;
  border: 2px solid #ffffff;
  border-radius: 50%;
  box-shadow: 0 0 0 3px rgba(255,0,0,0.3), 0 0 10px #ff0000;
  pointer-events: none;
}

.map-tooltip-footer {
  padding: 0.5rem 0.75rem;
  background: var(--bg, #f5f5f5);
  border-top: 1px solid var(--border, #e0e0e0);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.coordinates {
  font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
  font-size: 0.875rem;
  color: var(--text-light, #666666);
}

.zoom-hint {
  font-size: 0.75rem;
  color: var(--text-light, #666666);
}
</style>
