<script setup lang="ts">
import { nextTick, ref } from "vue"

const props = defineProps<{
  zoneIndex?: number
}>()

const emit = defineEmits<{
  activate: [content: string]
  focus: []
  blur: []
  "navigate-up": []
  "navigate-down": []
}>()

const hovered = ref(false)
const focused = ref(false)
const rootEl = ref<HTMLElement | null>(null)
const inputEl = ref<HTMLInputElement | null>(null)

/**
 * Keydown handler on the root `<div>`. Handles the full set of keys
 * including character-by-character text entry. This is the active
 * handler when the zone is focused programmatically (via `focusZone()`
 * in Editor.vue). When the `<input>` is the active element, events
 * are handled by `onInputKeydown` instead.
 */
function onKeydown(e: KeyboardEvent) {
  if (e.key === "ArrowUp") {
    e.preventDefault()
    emit("navigate-up")
    return
  }

  if (e.key === "ArrowDown") {
    e.preventDefault()
    emit("navigate-down")
    return
  }

  if (e.key === "Escape") {
    e.preventDefault()
    deactivate()
    return
  }

  if (e.key === "Enter") {
    e.preventDefault()
    emit("activate", "")
    return
  }

  if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
    e.preventDefault()
    emit("activate", e.key)
    return
  }
}

/**
 * Keydown handler for the `<input>` element. Only handles navigation
 * keys (ArrowUp/Down, Escape, Enter). Character keys are intentionally
 * NOT intercepted — they flow into the input naturally and are captured
 * by `onInput`. This avoids duplicate blocks on mobile where:
 * 1. The first character's `keydown` fires
 * 2. Autocorrect/IME replaces the field value
 * 3. `@input` fires with the full word
 * If character keys were handled here, steps 1 and 3 would each emit
 * `activate`, creating two blocks.
 */
function onInputKeydown(e: KeyboardEvent) {
  if (e.key === "ArrowUp") {
    e.preventDefault()
    emit("navigate-up")
    return
  }

  if (e.key === "ArrowDown") {
    e.preventDefault()
    emit("navigate-down")
    return
  }

  if (e.key === "Escape") {
    e.preventDefault()
    deactivate()
    return
  }

  if (e.key === "Enter") {
    e.preventDefault()
    const value = (e.target as HTMLInputElement).value
    emit("activate", value)
    return
  }
}

function onPaste(e: ClipboardEvent) {
  e.preventDefault()
  const text = e.clipboardData?.getData("text/plain") ?? ""
  emit("activate", text)
}

/**
 * Activate the zone: show the input and request focus. Uses `nextTick`
 * so the `<input>` is in the DOM before `.focus()` is called. A native
 * `<input>` is required here — `tabindex` on a `<div>` does not trigger
 * the mobile soft keyboard.
 */
function activate() {
  if (focused.value) return
  focused.value = true
  emit("focus")
  nextTick(() => inputEl.value?.focus())
}

function deactivate() {
  focused.value = false
  emit("blur")
}

function onClick() {
  activate()
}

/**
 * Deactivate only when focus leaves the entire zone subtree. Without
 * this check, focusing the child `<input>` would trigger `blur` on the
 * root `<div>` and immediately close the zone. `focusout` bubbles, and
 * `relatedTarget` tells us where focus went — if it's still inside the
 * zone (e.g. the input), we stay active.
 */
function onFocusout(e: FocusEvent) {
  if (!rootEl.value?.contains(e.relatedTarget as Node)) {
    deactivate()
  }
}

/**
 * Single source of truth for text entry. On desktop, characters arrive
 * one by one (each fires `activate`). On mobile, autocorrect may flush
 * the entire word at once — `activate` is called once with the full
 * value, avoiding duplicate blocks.
 */
function onInput(e: Event) {
  const value = (e.target as HTMLInputElement).value
  if (value) {
    emit("activate", value)
  }
}

defineExpose({
  focus: activate,
})
</script>

<template>
  <div
    ref="rootEl"
    role="button"
    aria-label="Insert new block"
    class="insertion-zone relative outline-none cursor-pointer transition-all duration-150"
    :data-zone-index="zoneIndex"
    :data-drop-target="''"
    :class="focused ? 'h-10' : 'h-1'"
    @keydown="onKeydown"
    @paste="onPaste"
    @click="onClick"
    @focusout="onFocusout"
    @mouseenter="hovered = true"
    @mouseleave="hovered = false"
  >
    <div
      v-if="hovered && !focused"
      class="absolute inset-0 flex items-center"
    >
      <USeparator
        icon="i-lucide-square-plus"
        class="w-full text-(--ui-text-muted)"
        :ui="{ icon: 'text-(--ui-text-muted)' }"
        color="neutral"
        type="dotted"
      />
    </div>

    <div
      v-if="focused"
      class="absolute inset-0 rounded-lg border-2 bg-(--ui-bg-elevated) flex items-center gap-2.5 px-4 shadow-sm border-(--ui-text-muted)/40"
    >
      <input
        ref="inputEl"
        class="w-full bg-transparent outline-none text-sm placeholder-(--ui-text-muted)"
        placeholder="Type to add a block..."
        @input="onInput"
        @keydown="onInputKeydown"
      />
    </div>
  </div>
</template>
