<!--
Line highlighting for code blocks.
(auto transformed, you don't need to use this component directly)

Usage:

```ts {1,3-5|2,4}
const your_code = 'here'
```

Learn more: https://sli.dev/guide/syntax.html#line-highlighting
-->

<script setup lang="ts">
import { range, remove } from '@antfu/utils'
import { parseRangeString } from '@slidev/parser/core'
import { computed, defineProps, getCurrentInstance, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import { injectionClicks, injectionClicksElements, injectionClicksDisabled, CLASS_VCLICK_TARGET } from '../modules/directives'

const props = defineProps({
  ranges: {
    default: () => [],
  },
  at: {
    type: Number,
    default: undefined,
  },
})

const clicks = inject(injectionClicks)
const elements = inject(injectionClicksElements)
const disabled = inject(injectionClicksDisabled)

function makeid(length = 5) {
  const result = []
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  const charactersLength = characters.length
  for (let i = 0; i < length; i++)
    result.push(characters.charAt(Math.floor(Math.random() * charactersLength)))
  return result.join('')
}

const el = ref<HTMLDivElement>()
const vm = getCurrentInstance()

onMounted(() => {
  const prev = props.at == null ? elements?.value.length : props.at
  const index = computed(() => {
    if (disabled?.value)
      return props.ranges.length - 1
    return Math.min(Math.max(0, (clicks?.value || 0) - (prev || 0)), props.ranges.length - 1)
  })
  const rangeStr = computed(() => props.ranges[index.value] || '')
  if (props.ranges.length >= 2 && !disabled?.value) {
    const id = makeid()
    const ids = range(props.ranges.length - 1).map(i => id + i)
    if (elements?.value) {
      elements.value.push(...ids)
      onUnmounted(() => ids.forEach(i => remove(elements.value, i)), vm)
    }
  }

  watchEffect(() => {
    if (!el.value)
      return
    const isDuoTone = el.value.querySelector('.shiki-dark')
    const targets = isDuoTone ? Array.from(el.value.querySelectorAll('.shiki')) : [el.value]
    for (const target of targets) {
      const lines = Array.from(target.querySelectorAll('.line'))
      const highlights: number[] = parseRangeString(lines.length, rangeStr.value)
      lines.forEach((line, idx) => {
        const highlighted = highlights.includes(idx + 1)
        line.classList.toggle(CLASS_VCLICK_TARGET, true)
        line.classList.toggle('highlighted', highlighted)
        line.classList.toggle('dishonored', !highlighted)
      })
    }
  })
})
</script>

<template>
  <div ref="el">
    <slot />
  </div>
</template>
