import { computed, getCurrentInstance, useSlots, type ComputedRef } from 'vue'

/**
 * CE-safe "does this named slot have real content?" detection. [INFRA-F54]
 *
 * WHY THIS EXISTS
 * ---------------
 * A canonical component that gates a named-slot outlet with `v-if="$slots.x"`
 * (or a `useSlots()`-derived guard) silently drops that slot in the
 * `defineCustomElement` (web-component / React consumer) build, while working
 * fine in the plain-Vue SFC build. Root cause (Vue 3.5 runtime-dom):
 *
 *   - `VueElement._createVNode()` builds the CE root vnode as
 *     `createVNode(def, props)` — with NO slot children. So the canonical root
 *     component's `$slots` / `useSlots()` are ALWAYS empty under CE, regardless
 *     of what light-DOM `[slot=x]` children the host actually has. Any
 *     `v-if="$slots.x"` guard is therefore permanently false → the
 *     `<slot name="x"/>` outlet never renders → nothing to project into.
 *   - Projection instead happens off the host element: for shadow-DOM CEs the
 *     browser projects the host's light-DOM `[slot=x]` children into the native
 *     `<slot>` element that `renderSlot` emits (it emits a real `<slot>` when
 *     `currentRenderingInstance.ce` is set); for light-DOM CEs
 *     (`shadowRoot:false`) `connectedCallback` calls `_parseSlots()` FIRST,
 *     moving the slotted children off the host into `host._slots` (keyed by the
 *     `slot` attribute), and `_renderSlots()` later fills the `<slot>` outlets.
 *
 * So the guard cannot ask `$slots`; it must ask the host. But we also cannot
 * drop the guard and forward unconditionally (like TopBar/ButtonBridge do),
 * because these components' base templates make the named slot COMPETE with a
 * prop / native-fallback (FormItem `<slot name=label>` vs `{{ labelText }}`,
 * Tooltip `<slot name=content>{{ content }}</slot>`, PopupBox
 * `<slot name=footer><Button/><Button/></slot>`). An always-rendered empty
 * outlet would shadow that fallback → regression in BOTH builds. We must render
 * the outlet ONLY when the host actually carries slotted content.
 *
 * DETECTION (evaluated synchronously at setup — see timing note below)
 * -------------------------------------------------------------------
 *   1. Plain-Vue / SFC build: compiled `useSlots()[name]` is authoritative and
 *      reactive — trust it (mirrors the pre-fix guard, so SFC behaviour is
 *      unchanged and non-regressing).
 *   2. Light-DOM CE (`shadowRoot:false`): `_parseSlots()` already ran in
 *      `connectedCallback` (BEFORE setup), so read `host._slots[name]`.
 *   3. Shadow-DOM CE (default): `_parseSlots()` is NOT called; the slotted
 *      children remain as light-DOM children of the host — query
 *      `host.querySelector(':scope > [slot="name"]')`.
 *
 * TIMING: all three signals are available synchronously during `setup()`
 * (`_parseSlots` and the host's initial children are populated before `_mount`
 * runs setup). An earlier async (`onMounted` ref) spike failed because the
 * reactive forwarding chain did not propagate in time; the synchronous read
 * here matches how the CE mounts its slots once per connect. CE slot content is
 * treated as static-per-mount (the same assumption TopBar's unconditional
 * forward relies on) — the returned computed has no reactive CE dependency, so
 * it evaluates once under CE and stays reactive to `useSlots()` under SFC.
 *
 * We intentionally read `getCurrentInstance().ce` directly rather than Vue's
 * `useHost()`: `useHost()` emits a dev warning when called outside a CE (i.e.
 * in every plain-Vue consumer), which would spam consumer consoles. `.ce` is
 * the exact field `useHost()` reads internally.
 */
export function useHasSlot(name: string): ComputedRef<boolean> {
  const slots = useSlots()
  // `.ce` (the custom-element host) is not on the public ComponentInternalInstance
  // type, but it is exactly what Vue's own `useHost()` reads internally. Cast to
  // reach it without pulling in `useHost()`'s dev warning (see doc block above).
  const instance = getCurrentInstance() as { ce?: unknown } | null
  const host = (instance?.ce ?? null) as
    | (HTMLElement & { _slots?: Record<string, unknown[]> })
    | null

  return computed<boolean>(() => {
    // 1. Plain-Vue / SFC: compiled slots are authoritative + reactive.
    if (slots[name]) return true

    // 2 + 3. Web-component build: $slots is empty by construction; ask the host.
    if (host) {
      // Light-DOM CE: children were moved into host._slots by _parseSlots().
      const parsed = host._slots
      if (parsed && Object.prototype.hasOwnProperty.call(parsed, name)) {
        const content = parsed[name]
        return Array.isArray(content) ? content.length > 0 : Boolean(content)
      }
      // Shadow-DOM CE: children remain as light-DOM children of the host.
      return Boolean(host.querySelector(`:scope > [slot="${name}"]`))
    }

    return false
  })
}
