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

const configuredHash = import.meta.env.VITE_EASY_SLIDES_PRESENTER_HASH || ''
const isPresenter = computed(() => /\/presenter(?:\/\d+)?\/?$/.test(window.location.pathname))
const storageKey = `easy-slides:presenter:${import.meta.env.BASE_URL}`
const unlocked = ref(false)
const token = ref('')
const error = ref('')
const noteSize = ref(20)
const contrast = ref(false)
const gate = ref<HTMLDialogElement>()
const tokenInput = ref<HTMLInputElement>()

onMounted(async () => {
  unlocked.value = sessionStorage.getItem(storageKey) === configuredHash
    || (!configuredHash && import.meta.env.DEV)
  syncBodyState()
  await syncPresenterGate()
})

onBeforeUnmount(() => {
  if (gate.value?.open)
    gate.value.close()
  document.body.classList.remove('easy-presenter-active', 'easy-presenter-locked', 'easy-notes-contrast')
})

watch(unlocked, () => void syncPresenterGate())

async function unlock() {
  error.value = ''
  const candidate = await sha256(token.value)
  if (candidate !== configuredHash) {
    error.value = 'Token 不正确'
    return
  }
  sessionStorage.setItem(storageKey, configuredHash)
  unlocked.value = true
  token.value = ''
  await nextTick()
  syncBodyState()
}

function lock() {
  sessionStorage.removeItem(storageKey)
  unlocked.value = false
  syncBodyState()
}

function openPresenter() {
  window.location.href = `${import.meta.env.BASE_URL}presenter/`
}

function resizeNotes(delta: number) {
  noteSize.value = Math.min(38, Math.max(14, noteSize.value + delta))
  document.documentElement.style.setProperty('--easy-presenter-note-size', `${noteSize.value}px`)
}

function toggleContrast() {
  contrast.value = !contrast.value
  syncBodyState()
}

function syncBodyState() {
  document.body.classList.toggle('easy-presenter-active', isPresenter.value && unlocked.value)
  document.body.classList.toggle('easy-notes-contrast', isPresenter.value && unlocked.value && contrast.value)
}

async function syncPresenterGate() {
  await nextTick()
  const shouldOpen = isPresenter.value && !unlocked.value
  document.body.classList.toggle('easy-presenter-locked', shouldOpen)
  if (!shouldOpen) {
    if (gate.value?.open)
      gate.value.close()
    return
  }
  if (gate.value && !gate.value.open)
    gate.value.showModal()
  tokenInput.value?.focus()
}

function keepGateOpen(event: Event) {
  event.preventDefault()
  tokenInput.value?.focus()
}

async function sha256(value: string) {
  const bytes = new TextEncoder().encode(value.trim())
  const digest = await crypto.subtle.digest('SHA-256', bytes)
  return Array.from(new Uint8Array(digest), byte => byte.toString(16).padStart(2, '0')).join('')
}
</script>

<template>
  <button
    v-if="!isPresenter && configuredHash"
    class="easy-presenter-launcher"
    type="button"
    title="打开演示者模式"
    @click="openPresenter"
  >
    演示者
  </button>

  <Teleport to="body">
    <dialog
      v-if="isPresenter && !unlocked"
      ref="gate"
      class="easy-presenter-gate"
      aria-labelledby="easy-presenter-title"
      @cancel="keepGateOpen"
      @click.stop
      @keydown.stop
      @keyup.stop
      @pointerdown.stop
    >
      <section class="easy-presenter-dialog">
        <h1 id="easy-presenter-title">演示者模式</h1>
        <p v-if="configuredHash">输入 token 后查看下一页、演讲备注和计时器。</p>
        <p v-else>该站点没有配置演示者 token。</p>
        <form v-if="configuredHash" @submit.prevent="unlock">
          <input ref="tokenInput" v-model="token" type="password" autocomplete="current-password" autofocus aria-label="演示者 token">
          <button type="submit">解锁</button>
        </form>
        <p v-if="error" class="easy-presenter-error" role="alert">{{ error }}</p>
      </section>
    </dialog>
  </Teleport>

  <nav v-if="isPresenter && unlocked" class="easy-presenter-tools" aria-label="提词器设置">
    <button type="button" title="减小讲稿字号" @click="resizeNotes(-2)">A−</button>
    <button type="button" title="增大讲稿字号" @click="resizeNotes(2)">A+</button>
    <button type="button" title="切换高对比讲稿" @click="toggleContrast">◐</button>
    <button type="button" title="锁定演示者模式" @click="lock">锁定</button>
  </nav>
</template>
