<script setup lang="ts">
import type { Ref, CSSProperties } from 'vue'
import type { ElTooltipProps, TooltipInstance } from 'element-plus'
import { getOffset, getPopperProps, scrollIntoView } from './util'
import type { TourStep } from './types'
import { tourEmits } from './props'
import type { TourProps } from './props'

const saveCatchKey = '_FS_TOUR'

const props = withDefaults(defineProps<TourProps>(), {
  once: false,
  mask: true,
  padding: 6
})

const emits = defineEmits(tourEmits)

/** 气泡触发组件 */
const triggerRef = ref<any>(null)

/** 气泡组件 */
const tooltipRef = ref<TooltipInstance | null>(null)

/** 气泡组件属性 */
const tooltipProps = shallowRef<Partial<ElTooltipProps>>({})

/** 是否打开引导 */
const visible = ref<boolean>(false)

/** 当前步骤 */
const step = shallowRef<TourStep | null>(null)

/** 是否是最后一步 */
const isLast = ref<boolean>(false)

/** 盒子样式 */
const boxStyle: Ref<CSSProperties> = ref<CSSProperties>({})

/** 是否显示遮罩 */
const showMask = ref<boolean>(false)

/** 开始引导 */
const start = () => {
  if (!props.steps || props.modelValue == null || props.modelValue < 0 || props.modelValue >= props.steps.length) {
    close()
    return
  }
  step.value = props.steps[props.modelValue]
  if (!step.value) {
    return
  }

  isLast.value = props.modelValue === props.steps.length - 1
  const { mask, popoverProps, target, padding } = step.value
  showMask.value = mask ?? props.mask
  const el = typeof target === 'function' ? target() : target
  if (el) {
    scrollIntoView(el)
    // 气泡形式
    const { width, height } = el.getBoundingClientRect()
    const { top, left } = getOffset(el)
    const space = padding ?? props.padding ?? 0
    boxStyle.value = {
      width: width + space + space + 'px',
      height: height + space + space + 'px',
      top: top - space + 'px',
      left: left - space + 'px'
    }
  } else {
    // 弹窗形式
    boxStyle.value = {
      width: '0px',
      height: '0px',
      top: '50%',
      left: '50%'
    }
  }

  if (props.once && props.tourKey && findKey()) {
    return
  }
  // 显示
  visible.value = true
  tooltipProps.value = getPopperProps(true, !el, popoverProps as any)
  nextTick(() => {
    updatePopper()
  })
  // 缓存一个key
  if (props.once && props.tourKey) {
    const catchTour = localStorage.getItem(saveCatchKey)
    if (catchTour) {
      const arr = JSON.parse(catchTour)
      arr.push(props.tourKey)
      localStorage.setItem(saveCatchKey, JSON.stringify(arr))
    } else {
      localStorage.setItem(saveCatchKey, JSON.stringify([props.tourKey]))
    }
  }
}

/* 查找缓存的key */
const findKey = () => {
  const catchKey = localStorage.getItem(saveCatchKey)
  if (!catchKey) {
    return false
  }
  const arr = JSON.parse(catchKey)
  return arr.find((item: string) => item === props.tourKey)
}

/** 关闭引导 */
const close = () => {
  visible.value = false
  boxStyle.value = {}
  step.value = null
  showMask.value = false
  tooltipProps.value = getPopperProps()
}

/** 更新步骤值 */
const updateModelValue = (value?: number | null) => {
  emits('update:modelValue', value)
}

/** 上一步 */
const onPrev = () => {
  if (props.modelValue != null && props.steps != null && props.steps.length && props.modelValue > 0) {
    updateModelValue(props.modelValue - 1)
  }
}

/** 下一步 */
const onNext = () => {
  if (
    props.modelValue != null &&
    props.steps != null &&
    props.steps.length &&
    props.modelValue < props.steps.length - 1
  ) {
    updateModelValue(props.modelValue + 1)
  }
}

/** 结束 */
const onFinish = () => {
  updateModelValue(null)
}

/** 更新气泡位置 */
const updatePopper = () => {
  tooltipRef.value && tooltipRef.value.updatePopper()
}

onMounted(() => {
  start()
})

watch(
  () => props.modelValue,
  () => {
    start()
  }
)
</script>

<template>
  <Teleport to="body">
    <div :class="['tour', { 'show-mask': showMask }, { 'is-open': visible }]" :style="{ zIndex: zIndex }">
      <div class="tour-box" :style="boxStyle"></div>
      <div ref="triggerRef" class="tour-reference" :style="boxStyle"></div>
      <el-tooltip
        v-bind="tooltipProps"
        ref="tooltipRef"
        :virtualRef="triggerRef"
        :virtualTriggering="true"
        :disabled="!visible"
      >
        <template #content>
          <div v-if="steps && step" class="popover-body">
            <div v-if="step.title" class="tour-title">
              <slot name="title" :step="step" :current="modelValue">
                {{ step.title }}
              </slot>
            </div>
            <div class="tour-text">
              <slot name="text" :step="step" :current="modelValue">
                {{ step.description }}
              </slot>
            </div>
            <slot name="footer" :step="step" :current="modelValue">
              <div class="tour-footer">
                <div class="tour-counter">{{ (modelValue || 0) + 1 }}/{{ steps.length }}</div>
                <div class="tour-action">
                  <el-button v-if="!isLast" size="small" @click="onFinish"> 跳过 </el-button>
                  <el-button v-if="modelValue !== 0" size="small" @click="onPrev"> 上一步 </el-button>
                  <el-button v-if="!isLast" size="small" type="primary" @click="onNext"> 下一步 </el-button>
                  <el-button v-if="isLast" size="small" type="primary" @click="onFinish"> 结束 </el-button>
                </div>
              </div>
            </slot>
          </div>
        </template>
      </el-tooltip>
    </div>
  </Teleport>
</template>

<style lang="scss">
.tour {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--el-index-popper);
  display: none;

  &.is-open {
    display: block;
  }
}

/* 带高亮的遮罩 */
.tour-box {
  position: absolute;
  border: 2px solid var(--el-color-primary);
  border-radius: var(--border-radius);
  transition: (left 0.2s, top 0.2s, width 0.2s, height 0.2s);
}

.tour.show-mask > .tour-box {
  border: none;
  box-shadow: 0 0 0 1000vw var(--el-overlay-color-lighter);
}

/* 气泡定位元素 */
.tour-reference {
  position: absolute;
  box-sizing: border-box;
}

/* 标题 */
.tour-title {
  font-weight: bold;
  color: var(--el-text-color-primary);
  margin-bottom: 8px;
}

/* 内容 */
.tour-text {
  font-size: var(--el-font-size-small);
}

/* 底部 */
.tour-footer {
  display: flex;
  align-items: center;
  margin-top: 16px;
}

/* 指示器 */
.tour-counter {
  color: var(--el-text-color-secondary);
  font-size: var(--el-font-size-extra-small);
}

/* 操作按钮 */
.tour-action {
  flex: 1;
  text-align: right;

  .el-button {
    padding-left: 8px;
    padding-right: 8px;

    & + .el-button {
      margin-left: 8px;
    }
  }
}

.tour-popover > .popover-body {
  padding: 12px 16px;
}

/* 气泡弹窗效果 */
.tour-popover.tour-modal {
  width: 500px;
  margin: 0 auto;
  left: 0 !important;
  top: 50% !important;
  right: 0 !important;
  bottom: auto !important;
  transform: translateY(-50.1%) !important;
  transform-origin: center !important;
  max-width: calc(100% - 32px);
  box-sizing: border-box;

  & > .el-popper__arrow {
    display: none;
  }
}
</style>
