<template>
  <div ref="target" :class="rootClass" :style="rootStyle" />
</template>

<script setup lang="ts">
import TypeIt from 'typeit'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'

export type TypeItTerminalSegment = {
  html: string
  speed?: number
  pauseBefore?: number
  pauseAfter?: number
  deleteAfter?: boolean
  deleteSpeed?: number
}

type Props = {
  segments?: TypeItTerminalSegment[]
  minLines?: number
  lineHeightEm?: number
  align?: 'top' | 'center' | 'bottom'
  startOnVisible?: boolean
  startDelay?: number
  cursor?: boolean
  cursorChar?: string
  loop?: boolean
  loopDelay?: number | [number, number]
}

const props = withDefaults(defineProps<Props>(), {
  minLines: 2,
  lineHeightEm: 1.65,
  align: 'top',
  startOnVisible: true,
  startDelay: 250,
  cursor: true,
  cursorChar: '\u258c',
  loop: true,
  loopDelay: () => [900, 1100],
})

const target = ref<HTMLElement | null>(null)

const segments = computed<TypeItTerminalSegment[]>(
  () =>
    props.segments ?? [
      {
        html: [
          '<div><span class="text-gray-500">$</span> <span class="text-green-600">npm</span> <span class="text-blue-600">run</span> build</div>',
          `<div><span class="text-gray-500">${'\u2713'}</span> <span class="text-orange-500">Compiling</span> <span class="text-gray-500">src/main.ts...</span></div>`,
        ].join(''),
        speed: 32,
      },
    ],
)

const rootClass = computed(() => {
  const alignClass =
    props.align === 'bottom' ? 'justify-end' : props.align === 'center' ? 'justify-center' : 'justify-start'
  return ['flex flex-col', alignClass].join(' ')
})

const rootStyle = computed(() => {
  if (!props.minLines) return undefined
  return {
    minHeight: `calc(${props.lineHeightEm}em * ${props.minLines})`,
  }
})

let typeit: TypeIt | null = null

onMounted(() => {
  if (!target.value) return
  target.value.innerHTML = ''

  const instance = new TypeIt(target.value, {
    startDelay: props.startDelay,
    lifeLike: true,
    cursor: props.cursor,
    cursorChar: props.cursorChar,
    html: true,
    waitUntilVisible: props.startOnVisible,
    loop: props.loop,
    loopDelay: props.loopDelay,
  })

  for (const segment of segments.value) {
    if (segment.pauseBefore) {
      instance.pause(segment.pauseBefore)
    }

    instance.type(segment.html, segment.speed ? { speed: segment.speed } : {})

    if (segment.pauseAfter) {
      instance.pause(segment.pauseAfter)
    }

    if (segment.deleteAfter) {
      instance.delete(undefined, segment.deleteSpeed ? { deleteSpeed: segment.deleteSpeed } : {})
    }
  }

  typeit = instance.go()
})

onBeforeUnmount(() => {
  typeit?.destroy()
  typeit = null
})
</script>
