<script setup lang="ts">
interface Props {
  type?: 'click' | 'hover'
  position?:
    | 'top'
    | 'bottom'
    | 'left'
    | 'right'
    | 'top-left'
    | 'top-right'
    | 'bottom-left'
    | 'bottom-right'
  offset?: number
  backgroundColor?: string
  textColor?: string
  textAlign?: string
  fontSize?: string
  fontWeight?: string
  lineHeight?: string
  letterSpacing?: string
  arrow?: boolean
  teleport?: boolean
  content?: string
}

const props = withDefaults(defineProps<Props>(), {
  type: 'click',
  position: 'top',
  offset: 8,
  arrow: true,
  teleport: false,
  backgroundColor: '#666666',
  textColor: '#FFFFFF',
  textAlign: 'center',
  fontSize: '12px',
  fontWeight: '400',
  lineHeight: '18px',
  letterSpacing: '-0.24px',
})

const isOpen = defineModel<boolean>('isOpen', { default: false })

const isLeftAligned = ref(false) // 브라우저 화면 기준으로 툴팁의 왼쪽 가장자리가 화면 밖으로 나가는 경우 좌측 정렬을 합니다.
const isRightAligned = ref(false) // 브라우저 화면 기준으로 툴팁의 오른쪽 가장자리가 화면 밖으로 나가는 경우 우측 정렬을 합니다.
const tooltipOffsetX = ref(18) // 툴팁 오프셋 X 값
const tooltipArrowOffsetX = ref(26) // 툴팁 화살표 오프셋 X 값
const tooltipPosition = ref({
  // 툴팁 위치 값
  top: null,
  bottom: null,
  left: null,
  right: null,
})
const tooltipTriggerRef = ref<HTMLElement | null>(null)

const isPositions = computed(() => {
  return {
    isTop:
      props.position === 'top' && !isLeftAligned.value && !isRightAligned.value,
    isTopLeft:
      props.position === 'top-left' ||
      (props.position === 'top' && isRightAligned.value),
    isTopRight:
      props.position === 'top-right' ||
      (props.position === 'top' && isLeftAligned.value),
    isBottom:
      props.position === 'bottom' &&
      !isLeftAligned.value &&
      !isRightAligned.value,
    isBottomLeft:
      props.position === 'bottom-left' ||
      (props.position === 'bottom' && isRightAligned.value),
    isBottomRight:
      props.position === 'bottom-right' ||
      (props.position === 'bottom' && isLeftAligned.value),
    isLeft: props.position === 'left',
    isRight: props.position === 'right',
  }
})

/**
 * 툴팁 초기화 함수입니다.
 */
const resetTooltipContent = () => {
  tooltipPosition.value.top = null
  tooltipPosition.value.bottom = null
  tooltipPosition.value.left = null
  tooltipPosition.value.right = null
  isOpen.value = false
  isLeftAligned.value = false
  isRightAligned.value = false
  tooltipTriggerRef.value = null
}

/**
 * 반응형 툴팁 위치 계산 함수입니다.
 * @param trigger 트리거 요소
 */
const calculateTooltipPosition = (trigger: HTMLElement) => {
  const triggerRect = trigger.getBoundingClientRect()
  const tooltipOffset = props.offset + 4 // 툴팁 오프셋
  const triggerCenterX = triggerRect.left + triggerRect.width / 2
  const triggerCenterY = triggerRect.top + triggerRect.height / 2

  const isLefts =
    isPositions.value.isLeft ||
    isPositions.value.isTopLeft ||
    isPositions.value.isBottomLeft ||
    isLeftAligned.value
  const isOnlyLeft =
    isPositions.value.isLeft &&
    Object.values(isPositions.value).filter(Boolean).length === 1

  const isRights =
    isPositions.value.isRight ||
    isPositions.value.isTopRight ||
    isPositions.value.isBottomRight ||
    isRightAligned.value
  const isOnlyRight =
    isPositions.value.isRight &&
    Object.values(isPositions.value).filter(Boolean).length === 1

  let topPosition: number | null = null
  const bottomPosition: number | null = null
  let leftPosition: number | null = null
  const rightPosition: number | null = null

  if (isLefts) {
    if (isOnlyLeft) {
      leftPosition = triggerRect.left - tooltipOffset
    } else {
      leftPosition = triggerRect.left + triggerRect.width + tooltipOffsetX.value
    }
  } else if (isRights) {
    if (isOnlyRight) {
      leftPosition = triggerRect.left + triggerRect.width + tooltipOffset
    } else {
      leftPosition = triggerRect.left - tooltipOffsetX.value
    }
  } else {
    leftPosition = triggerCenterX
  }

  if (
    isPositions.value.isBottom ||
    isPositions.value.isBottomLeft ||
    isPositions.value.isBottomRight
  ) {
    topPosition = triggerRect.top
  } else if (
    isPositions.value.isTop ||
    isPositions.value.isTopLeft ||
    isPositions.value.isTopRight
  ) {
    topPosition = triggerRect.top - tooltipOffset
  } else {
    topPosition = triggerCenterY
  }

  tooltipPosition.value = {
    top: topPosition,
    bottom: bottomPosition,
    left: leftPosition,
    right: rightPosition,
  }
}

/**
 * 좌측 정렬이 필요한지 확인 함수입니다.
 * 브라우저 화면 기준으로 툴팁의 왼쪽 가장자리가 화면 밖으로 나가는 경우 좌측 정렬을 합니다.
 */
const isValidLeftAligned = (trigger: HTMLElement): boolean => {
  const tooltipWidth = 280 // 툴팁 최대 너비
  const triggerRect = trigger.getBoundingClientRect()
  const tooltipOffset = props.offset + 4 // 툴팁 오프셋

  // trigger의 중앙 위치 계산
  const triggerCenterX = triggerRect.left + triggerRect.width / 2

  // 중앙 정렬 시 툴팁의 왼쪽 가장자리 위치
  const tooltipLeftEdge = triggerCenterX - tooltipWidth / 2

  return tooltipLeftEdge < tooltipOffset
}

/**
 * 우측 정렬이 필요한지 확인 함수입니다.
 * 브라우저 화면 기준으로 툴팁의 오른쪽 가장자리가 화면 밖으로 나가는 경우 우측 정렬을 합니다.
 */
const isValidRightAligned = (trigger: HTMLElement): boolean => {
  const tooltipWidth = 280 // 툴팁 최대 너비
  const triggerRect = trigger.getBoundingClientRect()
  const windowWidth = window.innerWidth
  const tooltipOffset = props.offset + 4 // 툴팁 오프셋

  // trigger의 중앙 위치 계산
  const triggerCenterX = triggerRect.left + triggerRect.width / 2

  // 중앙 정렬 시 툴팁의 오른쪽 가장자리 위치
  const tooltipRightEdge = triggerCenterX + tooltipWidth / 2

  return tooltipRightEdge > windowWidth - tooltipOffset
}

/**
 * 툴팁 표시 함수입니다.
 * @param event 이벤트 객체
 * @param text 툴팁 텍스트
 */
const handleTooltip = (event: MouseEvent, type: string) => {
  if (type !== props.type) {
    return
  }

  const trigger = (event.currentTarget || event.target) as HTMLElement

  isOpen.value = !isOpen.value

  if (isOpen.value) {
    tooltipTriggerRef.value = trigger
    isLeftAligned.value = isValidLeftAligned(trigger)
    isRightAligned.value = isValidRightAligned(trigger)
    calculateTooltipPosition(trigger)
  } else {
    resetTooltipContent()
  }
}

/**
 * 브라우저 리사이즈 시 툴팁 위치 재계산 함수입니다.
 */
const handleResize = () => {
  if (isOpen.value && tooltipTriggerRef.value) {
    isLeftAligned.value = isValidLeftAligned(tooltipTriggerRef.value)
    isRightAligned.value = isValidRightAligned(tooltipTriggerRef.value)
    calculateTooltipPosition(tooltipTriggerRef.value)
  }
}

onMounted(() => {
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})
</script>

<template>
  <div
    class="relative z-[100] inline-flex items-center justify-center"
    v-bind="$attrs"
  >
    <button
      type="button"
      @click="handleTooltip($event, 'click')"
      @mouseenter="handleTooltip($event, 'hover')"
      @mouseleave="handleTooltip($event, 'hover')"
    >
      <slot name="trigger" />

      <template v-if="!$slots.trigger">
        <AtomsIconsStateInfoCircleLine :size="20" color="#7F7F7F" />
      </template>
    </button>
    <div
      v-if="!teleport && isOpen"
      :class="[
        'absolute z-[10] flex items-center justify-center w-max max-w-[280px] py-[8px] px-[12px] rounded-[4px]',
      ]"
      :style="[
        isPositions.isTop
          ? `top: -${props.offset + 4}px; left: 50%; transform: translate(-50%, -100%)`
          : isPositions.isBottom
            ? `bottom: -${props.offset + 4}px; left: 50%; transform: translate(-50%, 100%)`
            : isPositions.isLeft
              ? `top: 50%; left: -${props.offset + 4}px; transform: translate(-100%, -50%)`
              : isPositions.isRight
                ? `top: 50%; right: -${props.offset + 4}px; transform: translate(100%, -50%)`
                : isPositions.isTopLeft
                  ? `top: -${props.offset + 4}px; right: -${tooltipOffsetX}px; transform: translate(0, -100%)`
                  : isPositions.isTopRight
                    ? `top: -${props.offset + 4}px; left: -${tooltipOffsetX}px; transform: translate(0, -100%)`
                    : isPositions.isBottomLeft
                      ? `bottom: -${props.offset + 4}px; right: -${tooltipOffsetX}px; transform: translate(0, 100%)`
                      : isPositions.isBottomRight
                        ? `bottom: -${props.offset + 4}px; left: -${tooltipOffsetX}px; transform: translate(0, 100%)`
                        : '',
        `background-color: ${backgroundColor};`,
      ]"
    >
      <slot name="panel" />

      <p
        v-if="!$slots.panel"
        v-dompurify-html="props.content"
        :class="[
          `relative flex items-center justify-center text-${props.textAlign} text-[${props.fontSize}] font-[${props.fontWeight}] leading-[${props.lineHeight}] tracking-[${props.letterSpacing}]`,
        ]"
        :style="[`color: ${props.textColor};`]"
      ></p>

      <span
        v-if="arrow"
        class="absolute"
        :style="[
          isPositions.isTop
            ? `bottom: -4px; left: 50%; transform: translate(-50%, 0)`
            : isPositions.isBottom
              ? `top: -4px; left: 50%; transform: translate(-50%, 0) rotate(180deg)`
              : isPositions.isLeft
                ? `right: -4px; top: 50%; transform: translate(0, -50%) rotate(-90deg)`
                : isPositions.isRight
                  ? `left: -4px; top: 50%; transform: translate(0, -50%) rotate(90deg)`
                  : isPositions.isTopLeft
                    ? `bottom: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
                    : isPositions.isTopRight
                      ? `bottom: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
                      : isPositions.isBottomLeft
                        ? `top: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
                        : isPositions.isBottomRight
                          ? `top: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
                          : '',
        ]"
      >
        <svg
          width="6"
          height="4"
          viewBox="0 0 6 4"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M2.2 2.93333L0 0H6L3.8 2.93333C3.4 3.46667 2.6 3.46667 2.2 2.93333Z"
            :fill="backgroundColor"
          />
        </svg>
      </span>
    </div>
  </div>

  <ClientOnly>
    <Teleport to="body">
      <div
        v-if="teleport && isOpen"
        :class="[
          'absolute z-[100] flex items-center justify-center w-[280px] py-[8px] px-[12px] rounded-[4px] text-center text-[12px] font-[400] leading-[18px] tracking-[-0.24px] sm:w-auto sm:max-w-[280px]',
        ]"
        :style="[
          isPositions.isTop
            ? `transform: translate(-50%, -100%)`
            : isPositions.isBottom
              ? `transform: translate(-50%, 100%)`
              : isPositions.isLeft
                ? `transform: translate(0, -50%)`
                : isPositions.isRight
                  ? `transform: translate(0, -50%)`
                  : isPositions.isTopLeft
                    ? `transform: translate(-100%, -100%)`
                    : isPositions.isTopRight
                      ? `transform: translate(0, -100%)`
                      : isPositions.isBottomLeft
                        ? `transform: translate(-100%, 100%)`
                        : isPositions.isBottomRight
                          ? `transform: translate(0, 100%)`
                          : '',
          `top: ${tooltipPosition.top !== null ? `${tooltipPosition.top}px` : 'auto'}; bottom: ${tooltipPosition.bottom !== null ? `${tooltipPosition.bottom}px` : 'auto'}; left: ${tooltipPosition.left !== null ? `${tooltipPosition.left}px` : 'auto'}; right: ${tooltipPosition.right !== null ? `${tooltipPosition.right}px` : 'auto'}; background-color: ${backgroundColor};`,
        ]"
      >
        <slot name="panel" />

        <p
          v-if="!$slots.panel"
          v-dompurify-html="props.content"
          :class="[
            `relative flex items-center justify-center w-full text-${props.textAlign} text-[${props.fontSize}] font-[${props.fontWeight}] leading-[${props.lineHeight}] tracking-[${props.letterSpacing}]`,
          ]"
          :style="[`color: ${props.textColor};`]"
        ></p>

        <span
          v-if="arrow"
          class="absolute"
          :style="[
            isPositions.isTop
              ? `bottom: -4px; left: 50%; transform: translate(-50%, 0)`
              : isPositions.isBottom
                ? `top: -4px; left: 50%; transform: translate(-50%, 0) rotate(180deg)`
                : isPositions.isLeft
                  ? `right: -4px; top: 50%; transform: translate(0, -50%) rotate(-90deg)`
                  : isPositions.isRight
                    ? `left: -4px; top: 50%; transform: translate(0, -50%) rotate(90deg)`
                    : isPositions.isTopLeft
                      ? `bottom: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
                      : isPositions.isTopRight
                        ? `bottom: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
                        : isPositions.isBottomLeft
                          ? `top: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
                          : isPositions.isBottomRight
                            ? `top: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
                            : '',
          ]"
        >
          <svg
            width="6"
            height="4"
            viewBox="0 0 6 4"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M2.2 2.93333L0 0H6L3.8 2.93333C3.4 3.46667 2.6 3.46667 2.2 2.93333Z"
              :fill="backgroundColor"
            />
          </svg>
        </span>
      </div>
    </Teleport>
  </ClientOnly>
</template>

<style scoped></style>
