<template>
  <div
    ref="FloatingMenuListRef"
    v-if="isVisible"
    class="floating-menu"
    :style="{
      left: menuPosition.x + 'px',
      top: menuPosition.y + 'px',
      opacity: menuPosition.opacity,
      display: menuPosition.display
    }"
  >
    <slot name="default" :menuItemClick="handleMenuItemClick"> </slot>
  </div>
</template>

<script lang="ts" setup name="LjFloatingMenu">
import { ref, nextTick } from "vue";
import { classNameIncludes } from "@/utils/index";

const menuPosition = ref({ x: 0, y: 0, opacity: 1, display: "block" });
const isVisible = ref(false);
const FloatingMenuListRef = ref();
const emits = defineEmits(["menuItemClick"]);
const target = ref<any>(null);

const handleDocumentClick = (event: MouseEvent) => {
  let oDivMenu = document.querySelector(".floating-menu");
  if (oDivMenu && !event.composedPath().includes(oDivMenu)) {
    isVisible.value = false;
  }
};

const showMenu = (event: any, origin = false) => {
  isVisible.value = true;
  target.value = event;
  nextTick(() => {
    const { offsetWidth, offsetHeight } = FloatingMenuListRef.value;
    let _x = event.clientX;
    let _y = event.clientY - offsetHeight;

    if (event.target && !origin) {
      let { x, y } = event.target.getBoundingClientRect();
      _x = event?.clientX ?? x;
      _y = y - offsetHeight;
    }

    // 检查下方是否有足够的空间
    if (_y + offsetHeight > window.innerHeight) {
      _y -= offsetHeight + 10; // 调整以留出一些边距
    } else {
      // _y += 10; // 调整以留出一些边距
    }

    // console.log("X:", _x + offsetWidth, window.innerWidth, offsetWidth);
    // 检查右侧是否有足够的空间
    if (_x + offsetWidth > window.innerWidth) {
      _x -= offsetWidth + 10; // 调整以留出一些边距
    }

    // 检查上方是否有足够的空间
    if (_y < 0) {
      _y = 0; // 将菜单置于屏幕顶部
    }

    menuPosition.value = { x: _x, y: _y, opacity: 1, display: "block" };
    console.log("menuPosition.value: ", menuPosition.value);
    document.addEventListener("click", handleDocumentClick);
    document.removeEventListener("mousemove", trackMousePosition);
    document.addEventListener("mousemove", trackMousePosition);
  });
};

const hideMenu = () => {
  isVisible.value = false;
  document.removeEventListener("click", handleDocumentClick);
  document.removeEventListener("mousemove", trackMousePosition);
};

const handleMenuItemClick = (action: string, cb?: any) => {
  emits("menuItemClick", target.value, action, cb, () => {
    hideMenu();
  });
};

const trackMousePosition = (event: MouseEvent) => {
  let { clientX, clientY, target } = event;

  if (FloatingMenuListRef.value) {
    const { x, y } = menuPosition.value;
    const { offsetWidth, offsetHeight } = FloatingMenuListRef.value;

    let ifInside = classNameIncludes(target, ["floating-menu"]);
    if (ifInside) {
      menuPosition.value.opacity = 1;
      return;
    }

    // 计算点到矩形的最短距离
    let distance = calculateShortestDistanceToRectangle(
      {
        x: clientX,
        y: clientY
      },
      {
        x1: x,
        x2: x + offsetWidth,
        y1: y,
        y2: y + offsetHeight
      }
    );

    // console.log("distance :>> ", distance);

    // 根据距离调整透明度
    if (distance <= 50) {
      menuPosition.value.opacity = 1;
    } else if (distance > 50 && distance <= 150) {
      let opacity = 1 - (distance - 50) / 100;
      menuPosition.value.opacity = Math.max(0, opacity);
    } else {
      menuPosition.value.opacity = 0;
    }

    if (menuPosition.value.opacity <= 0) {
      hideMenu();
    }
  }
};

const calculateShortestDistanceToRectangle = (point: any, rect: any) => {
  const { x: pointX, y: pointY } = point;
  const { x1, y1, x2, y2 } = rect;
  let distance = Infinity;

  // // 点在矩形内
  // if (pointX >= x1 && pointX <= x2 && pointY >= y1 && pointY <= y2) {
  //   return 0;
  // }
  // 点在矩形外
  const edges = [
    { x1: x1, y1: y1, x2: x2, y2: y1 }, // 上边
    { x1: x1, y1: y2, x2: x2, y2: y2 }, // 下边
    { x1: x1, y1: y1, x2: x1, y2: y2 }, // 左边
    { x1: x2, y1: y1, x2: x2, y2: y2 } // 右边
  ];

  edges.forEach(edge => {
    const edgeDistance = linePointDistance(point, edge);
    distance = Math.min(distance, edgeDistance);
  });

  return distance;
};

function linePointDistance(point: any, line: any) {
  const { x: pointX, y: pointY } = point;
  const { x1, y1, x2, y2 } = line;
  const dx = x2 - x1;
  const dy = y2 - y1;
  const t = ((pointX - x1) * dx + (pointY - y1) * dy) / (dx * dx + dy * dy);

  if (t < 0) {
    return Math.sqrt((pointX - x1) ** 2 + (pointY - y1) ** 2);
  } else if (t > 1) {
    return Math.sqrt((pointX - x2) ** 2 + (pointY - y2) ** 2);
  }

  const cx = x1 + t * dx;
  const cy = y1 + t * dy;
  return Math.sqrt((cx - pointX) ** 2 + (cy - pointY) ** 2);
}

defineExpose({
  showMenu,
  hideMenu,
  handleMenuItemClick
});
</script>

<style lang="scss" scoped>
.floating-menu {
  position: fixed;
  border-radius: $br-sm;
  box-shadow: $shadow-1-down;
  background-color: $color-gray-1;
  // transform: translateY(-100%);
  z-index: 999;
  padding: $space-a1;
}
.floating-menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
}
.floating-menu li {
  padding: 5px 10px;
  cursor: pointer;
}
.floating-menu li:hover {
  background-color: #f0f0f0;
}
</style>
