<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
import { computePosition, offset, flip, shift, arrow, autoUpdate } from "@floating-ui/dom";
import { useNamespace, useId, useEventListener } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { tooltipProps, tooltipEmits } from "./tooltip";
defineOptions({
  name: "YhTooltip"
});
const props = defineProps(tooltipProps);
const emit = defineEmits(tooltipEmits);
const ns = useNamespace("tooltip");
const tooltipId = useId();
const { themeStyle } = useComponentTheme(
  "tooltip",
  computed(() => props.themeOverrides)
);
const triggerRef = ref(null);
const popperRef = ref(null);
const arrowRef = ref(null);
const visible = ref(false);
const popperStyle = ref({});
const arrowStyle = ref({});
const computedPopperStyle = computed(() => {
  const styles = {
    ...themeStyle.value,
    ...popperStyle.value
  };
  if (typeof props.popperStyle === "object") {
    Object.assign(styles, props.popperStyle);
  }
  return styles;
});
const computedContentStyle = computed(() => {
  const styles = {
    width: typeof props.width === "number" ? `${props.width}px` : props.width,
    maxHeight: typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight,
    overflowY: props.scrollable ? "auto" : "visible"
  };
  if (typeof props.contentStyle === "object") {
    Object.assign(styles, props.contentStyle);
  }
  return styles;
});
const actualPlacement = ref(props.placement);
let cleanup = null;
let showTimer = null;
let hideTimer = null;
const showPopper = computed(() => {
  if (props.disabled) return false;
  return props.visible !== null ? props.visible : visible.value;
});
const shouldRender = computed(() => props.persistent || showPopper.value);
const popperClasses = computed(() => [
  ns.e("popper"),
  ns.is(props.effect, true),
  // 使用 is-dark / is-light 更加稳定
  ns.em("popper", props.effect),
  ns.is("interactive", props.interactive),
  props.popperClass
]);
const updatePosition = async () => {
  if (!triggerRef.value || !popperRef.value || typeof window === "undefined") return;
  if (props.showArrow && !arrowRef.value) return;
  const { x, y, placement, middlewareData } = await computePosition(
    triggerRef.value,
    popperRef.value,
    {
      placement: props.placement,
      middleware: [
        offset(props.offset[1]),
        flip(),
        shift({ padding: 5 }),
        props.showArrow ? arrow({ element: arrowRef.value }) : null
      ].filter((item) => item !== null)
    }
  );
  popperStyle.value = {
    left: `${x}px`,
    top: `${y}px`,
    zIndex: String(props.zIndex)
  };
  actualPlacement.value = placement;
  if (middlewareData.arrow && arrowRef.value) {
    const { x: ax, y: ay } = middlewareData.arrow;
    const side = placement.split("-")[0];
    const staticSide = {
      top: "bottom",
      right: "left",
      bottom: "top",
      left: "right"
    }[side];
    arrowStyle.value = {
      left: ax != null ? `${ax}px` : "",
      top: ay != null ? `${ay}px` : "",
      [staticSide]: "-12px"
    };
  }
};
const handleMouseMove = async (e) => {
  if (!props.followCursor || !visible.value || typeof window === "undefined") return;
  const virtualElement = {
    getBoundingClientRect: () => {
      const rect = {
        width: 0,
        height: 0,
        x: e.clientX,
        y: e.clientY,
        top: e.clientY,
        left: e.clientX,
        right: e.clientX,
        bottom: e.clientY,
        toJSON: () => ({})
      };
      return rect;
    }
  };
  const { x, y } = await computePosition(virtualElement, popperRef.value, {
    placement: props.placement,
    middleware: [offset(10), flip(), shift()]
  });
  popperStyle.value = {
    left: `${x}px`,
    top: `${y}px`,
    zIndex: String(props.zIndex)
  };
};
const toggleVisible = (value) => {
  if (props.disabled) return;
  if (showTimer) {
    clearTimeout(showTimer);
    showTimer = null;
  }
  if (hideTimer) {
    clearTimeout(hideTimer);
    hideTimer = null;
  }
  if (value) {
    const delay = props.showAfter;
    if (delay <= 0) {
      visible.value = true;
      emit("update:visible", true);
      emit("show");
      nextTick(startAutoUpdate);
    } else {
      showTimer = setTimeout(() => {
        visible.value = true;
        emit("update:visible", true);
        emit("show");
        nextTick(startAutoUpdate);
      }, delay);
    }
  } else {
    const delay = props.hideAfter;
    if (delay <= 0) {
      visible.value = false;
      emit("update:visible", false);
      emit("hide");
      stopAutoUpdate();
    } else {
      hideTimer = setTimeout(() => {
        visible.value = false;
        emit("update:visible", false);
        emit("hide");
        stopAutoUpdate();
      }, delay);
    }
  }
};
const startAutoUpdate = async () => {
  if (cleanup) cleanup();
  if (triggerRef.value && popperRef.value && !props.followCursor && typeof window !== "undefined") {
    cleanup = autoUpdate(triggerRef.value, popperRef.value, updatePosition);
  }
};
const stopAutoUpdate = () => {
  if (cleanup) {
    cleanup();
    cleanup = null;
  }
};
const triggers = computed(() => {
  const t = Array.isArray(props.trigger) ? props.trigger : [props.trigger];
  return new Set(t);
});
const handleTrigger = (e, type) => {
  if (!triggers.value.has(type)) return;
  if (type === "hover") {
    toggleVisible(true);
  } else if (type === "click") {
    const isShowing = visible.value && !hideTimer || showTimer;
    toggleVisible(!isShowing);
  } else if (type === "contextmenu") {
    e.preventDefault();
    toggleVisible(true);
  } else if (type === "focus") {
    toggleVisible(true);
  }
};
useEventListener(
  () => window,
  "click",
  (e) => {
    const me = e;
    if (!visible.value) return;
    const needsClose = triggers.value.has("click") || triggers.value.has("contextmenu");
    if (!needsClose) return;
    const target = me.target;
    if (!triggerRef.value?.contains(target) && !popperRef.value?.contains(target)) {
      toggleVisible(false);
    }
  }
);
watch(
  () => props.visible,
  (val) => {
    if (val !== null && val !== visible.value) {
      visible.value = val;
      if (val) nextTick(startAutoUpdate);
      else stopAutoUpdate();
    }
  },
  { immediate: true }
);
watch(
  () => props.followCursor,
  (val) => {
    if (typeof window === "undefined") return;
    if (val) {
      window.addEventListener("mousemove", handleMouseMove);
    } else {
      window.removeEventListener("mousemove", handleMouseMove);
    }
  }
);
onMounted(() => {
  if (props.followCursor && typeof window !== "undefined") {
    window.addEventListener("mousemove", handleMouseMove);
  }
});
onUnmounted(() => {
  stopAutoUpdate();
  cleanup = null;
  if (showTimer) {
    clearTimeout(showTimer);
    showTimer = null;
  }
  if (hideTimer) {
    clearTimeout(hideTimer);
    hideTimer = null;
  }
  if (typeof window !== "undefined") {
    window.removeEventListener("mousemove", handleMouseMove);
  }
});
defineExpose({
  updatePosition,
  visible,
  triggerRef,
  popperRef
});
</script>

<template>
  <div
    :class="ns.b()"
    ref="triggerRef"
    @mouseenter="handleTrigger($event, 'hover')"
    @mouseleave="triggers.has('hover') && toggleVisible(false)"
    @click="handleTrigger($event, 'click')"
    @contextmenu="handleTrigger($event, 'contextmenu')"
    @focusin="handleTrigger($event, 'focus')"
    @focusout="triggers.has('focus') && toggleVisible(false)"
  >
    <slot />

    <Teleport to="body" :disabled="!teleported">
      <Transition :name="transition">
        <div
          v-if="shouldRender"
          v-show="showPopper"
          :id="tooltipId"
          ref="popperRef"
          :class="popperClasses"
          :style="computedPopperStyle"
          :data-placement="actualPlacement"
          @mouseenter="interactive && toggleVisible(true)"
          @mouseleave="interactive && triggers.has('hover') && toggleVisible(false)"
        >
          <div :class="[ns.e('content'), props.contentClass]" :style="computedContentStyle">
            <slot name="content">
              <!-- eslint-disable-next-line vue/no-v-html -->
              <span v-if="rawContent" v-html="content" />
              <span v-else>{{ content }}</span>
            </slot>
          </div>
          <!-- 小三角 - 使用 Floating UI 官方推荐的 SVG 路径方案 -->
          <div v-if="showArrow" ref="arrowRef" :class="ns.e('arrow-wrapper')" :style="arrowStyle">
            <svg
              :class="ns.e('arrow')"
              width="12"
              height="12"
              viewBox="0 0 12 12"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path d="M0,0 L6,6 L12,0" />
            </svg>
          </div>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
/* 
  YH-UI 智能自适应主题引擎
  逻辑优先级：显式强制设置 > 全局环境感应 (html.dark) > 默认浅色
*/
.yh-tooltip {
  display: inline-block;
  line-height: var(--yh-line-height-none);
}
.yh-tooltip__popper {
  position: absolute;
  z-index: var(--yh-z-index-tooltip);
  font-size: var(--yh-font-size-sm);
  line-height: 1.6;
  border-radius: var(--yh-radius-md);
  max-width: 350px;
  word-wrap: break-word;
  box-shadow: var(--yh-shadow-lg);
  pointer-events: none;
  backdrop-filter: blur(12px) saturate(180%);
  transition: transform 0.2s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.2s ease, background-color 0.15s ease, border-color 0.15s ease;
  overflow: visible;
  /* 默认共享变量：确保 SVG 始终引用同一套变量 */
  --yh-tooltip-bg: var(--yh-bg-color-overlay);
  --yh-tooltip-border: var(--yh-border-color-light);
  --yh-tooltip-text: var(--yh-text-color-primary);
}
.yh-tooltip__popper.is-interactive {
  pointer-events: auto;
}
.yh-tooltip__popper {
  /* --- 1. 基础亮色样式 --- */
  background: var(--yh-tooltip-bg);
  color: var(--yh-tooltip-text);
  border: 1px solid var(--yh-tooltip-border);
  /* --- 2. 强行设定：暗黑模式 (Explicit) --- */
}
.yh-tooltip__popper.is-dark, .yh-tooltip__popper.yh-popover__popper--dark {
  --yh-tooltip-bg: var(--yh-text-color-primary);
  --yh-tooltip-border: transparent;
  --yh-tooltip-text: var(--yh-bg-color);
  background: var(--yh-tooltip-bg);
  color: var(--yh-tooltip-text);
  border: 1px solid var(--yh-tooltip-border);
}
.yh-tooltip__popper {
  /* --- 3. 核心：全局环境感应方案 --- */
  /* 当 HTML 或 Body 带有 .dark 类名时，强制切换变量 */
}
:root.dark .yh-tooltip__popper, html.dark .yh-tooltip__popper, body.dark .yh-tooltip__popper {
  --yh-tooltip-bg: var(--yh-bg-color-overlay);
  --yh-tooltip-border: var(--yh-border-color-light);
  --yh-tooltip-text: var(--yh-text-color-primary);
  background: var(--yh-tooltip-bg);
  color: var(--yh-tooltip-text);
  border: 1px solid var(--yh-tooltip-border);
  /* 重写阴影，增强对比 */
  box-shadow: var(--yh-shadow-xl);
}

.yh-tooltip {
  /* 内容区域适配 */
}
.yh-tooltip__content {
  padding: 8px 12px;
  box-sizing: border-box;
  color: inherit;
}

.yh-tooltip {
  /* SVG 小三角：通过 fill/stroke 强制同步变量 */
}
.yh-tooltip__arrow-wrapper {
  position: absolute;
  width: 12px;
  height: 12px;
  z-index: 5;
  fill: var(--yh-tooltip-bg) !important;
  stroke: var(--yh-tooltip-border) !important;
  pointer-events: none;
}

.yh-tooltip__arrow {
  display: block;
  fill: inherit !important;
  stroke: inherit !important;
  stroke-width: 1px;
  margin-top: -1px;
  transform-origin: center;
}

/* 动画系统 */
[data-placement^=top] .yh-tooltip__arrow {
  transform: rotate(0deg);
}

[data-placement^=bottom] .yh-tooltip__arrow {
  transform: rotate(180deg);
}

[data-placement^=left] .yh-tooltip__arrow {
  transform: rotate(-90deg);
}

[data-placement^=right] .yh-tooltip__arrow {
  transform: rotate(90deg);
}

.yh-tooltip-fade-enter-from,
.yh-tooltip-fade-leave-to {
  opacity: 0;
  transform: scale(0.96);
}
</style>
