<template>
  <Transition name="tooltip-fade">
    <div v-show="visible" ref="tooltipElement" :class="classes" :style="style">
      <slot></slot>
    </div>
  </Transition>
</template>

<script setup lang="ts">
import { TooltipDirection } from '@/models';
import { computed, nextTick, onMounted, Ref, ref, watch } from 'vue';
import { useTooltipHelper } from './tooltip.helper';

import {
  MaybeComputedElementRef,
  useElementBounding,
  useWindowSize,
} from '@vueuse/core';

const tooltipElement = ref(null);
const tooltipElementBounding = useElementBounding(
  tooltipElement as MaybeComputedElementRef,
);
const calculatedTooltipTransform: Ref<string> = ref<string>('none');
const windowSize = useWindowSize();
const { getTooltipTranslatePosition } = useTooltipHelper();

import { useAttrs } from 'vue';
const attrs = useAttrs();

const props = withDefaults(
  defineProps<{
    visible?: boolean;
    direction?: TooltipDirection;
    // Callback parce que le calcul de position a besoin d'être fait
    // au moment ou l'on affiche le tooltip. Les computed & composables
    // ne déclenchent pas le re-calcul au bon moment du render.
    getAnchorElementValues: () =>
      | undefined
      | {
          top: number;
          left: number;
          width: number;
          height: number;
          right: number;
          bottom: number;
        };
  }>(),
  {
    visible: true,
    direction: TooltipDirection.Bottom,
  },
);
const anchorElement = ref<
  | undefined
  | {
      top: number;
      left: number;
      width: number;
      height: number;
      right: number;
      bottom: number;
    }
>(undefined);

const classes = computed(
  () =>
    `bms-tooltip__text bms-tooltip__text--${props.direction} ${attrs.class ? attrs.class : ''}`,
);

const setTooltipPosition = async () => {
  if (
    props.visible &&
    JSON.stringify(anchorElement.value) !==
      JSON.stringify(props.getAnchorElementValues())
  ) {
    anchorElement.value = props.getAnchorElementValues();

    // reset position
    calculatedTooltipTransform.value = 'none';

    if (!anchorElement.value) return;

    // we wait for the tooltip to be mounted and have a width & height to base our calculation from
    await nextTick();

    const { translateX, translateY } = getTooltipTranslatePosition(
      props.direction,
      {
        width: windowSize.width.value,
        height: windowSize.height.value,
      },
      anchorElement.value,
      {
        width: tooltipElementBounding.width.value,
        height: tooltipElementBounding.height.value,
        left: tooltipElementBounding.left.value,
        right: tooltipElementBounding.right.value,
        top: tooltipElementBounding.top.value,
        bottom: tooltipElementBounding.bottom.value,
      },
    );
    calculatedTooltipTransform.value = `translate(${Math.floor(translateX)}px,${Math.floor(translateY)}px)`;
  }
};

watch(() => props.visible, setTooltipPosition);
onMounted(setTooltipPosition);

const style = computed(() => {
  const anchorElement = props.getAnchorElementValues();
  if (!anchorElement) return;

  return {
    top: `${anchorElement.top}px`,
    left: `${anchorElement.left}px`,
    '--bms-tooltip-transform': calculatedTooltipTransform.value,
  };
});
</script>

<style lang="scss" scoped>
.bms-tooltip__text {
  --bms-tooltip-background-color: var(--bms-white);
  --bms-tooltip-color: inherit;
  --bms-tooltip-margin: 4px;

  background-color: var(--bms-tooltip-background-color);
  width: max-content;
  text-align: center;
  padding: 4px 8px;
  border-radius: var(--bms-border-radius);
  max-width: var(--bms-tooltip-max-width, 30rem);
  max-height: 30rem;
  position: absolute;
  overflow: hidden;
  z-index: var(--bms-z-index-tooltip);
  box-shadow: var(--bms-box-shadow-elevated);
  color: var(--bms-font-color);
  transform: var(--bms-tooltip-transform);
  overflow-wrap: break-word;

  &.floating-window__expand-btn__bms-tooltip,
  &.bms-in-modal {
    z-index: calc(var(--bms-z-index-modal) + 1);
  }

  &::before {
    content: '';
    position: absolute;
    right: var(--bms-tooltip-indicator-position-right);
    left: var(--bms-tooltip-indicator-position-left);
    top: var(--bms-tooltip-indicator-position-top);
    bottom: var(--bms-tooltip-indicator-position-bottom);
    transform: var(--bms-tooltip-indicator-transform);
    border-width: 3px;
    border-style: solid;
    border-color: var(--bms-tooltip-indicator-border);
  }

  &--top,
  &--bottom {
    --bms-tooltip-indicator-position-left: 50%;
    --bms-tooltip-indicator-position-right: initial;
  }

  &--bottom {
    --bms-tooltip-indicator-position-top: initial;
    --bms-tooltip-indicator-position-bottom: 100%;
    --bms-tooltip-indicator-border: transparent transparent
      var(--bms-tooltip-background-color) transparent;
  }

  &--top {
    --bms-tooltip-indicator-position-top: 100%;
    --bms-tooltip-indicator-position-bottom: initial;
    --bms-tooltip-indicator-border: var(--bms-tooltip-background-color)
      transparent transparent transparent;
  }

  &--left,
  &--right {
    --bms-tooltip-indicator-position-top: 50%;
    --bms-tooltip-indicator-position-bottom: initial;
  }

  &--left {
    --bms-tooltip-indicator-position-right: initial;
    --bms-tooltip-indicator-position-left: 100%;
    --bms-tooltip-indicator-border: transparent transparent transparent
      var(--bms-tooltip-background-color);
  }

  &--right {
    --bms-tooltip-indicator-position-right: 100%;
    --bms-tooltip-indicator-position-left: initial;
    --bms-tooltip-indicator-border: transparent
      var(--bms-tooltip-background-color) transparent transparent;
  }
}

.tooltip-fade-enter {
  &-active {
    transition: opacity 0.2s ease;
  }

  &-from {
    opacity: 0;
  }

  &-to {
    opacity: 1;
  }
}

.tooltip-fade-leave {
  &-active {
    transition: opacity 0.3s ease;
  }

  &-from {
    opacity: 1;
  }

  &-to {
    opacity: 0;
  }
}
</style>
