import { computed, getCurrentInstance, inject, onMounted, ref, type ComputedRef, type InjectionKey } from 'vue'
import type { FormEngine } from './form/formEngine'

// SFC path uses provide/inject. Under defineCustomElement, provide/inject does
// NOT cross the CE boundary, so Form also exposes its engine on the host element
// (host._tvuFormEngine) and FormItem discovers it via closest('tvu-form').
export const FormContextKey: InjectionKey<FormEngine> = Symbol('tvu-form-engine')

interface FormHost extends HTMLElement {
  _tvuFormEngine?: FormEngine
}

/**
 * Resolve the parent Form's engine, CE-safely. Returns null when the FormItem is
 * used standalone (no parent Form) — callers must treat that as "no validation"
 * so standalone FormItem behaviour is unchanged (backward compatible).
 *
 *  1. SFC / plain-Vue: `inject(FormContextKey)` — authoritative when present.
 *  2. defineCustomElement: read `getCurrentInstance().ce` (the host, exactly what
 *     Vue's useHost() reads) → `host.closest('tvu-form')` → parent host's
 *     `_tvuFormEngine`. `.ce` is read directly (not via useHost()) to avoid the
 *     dev warning useHost() emits in every plain-Vue consumer (see useHasSlot.ts).
 *     [Stage 0 spike validated: closest + host property + subscribe cross the CE
 *     boundary in a real browser.]
 */
export function useFormEngine(): FormEngine | null {
  const injected = inject(FormContextKey, null)
  if (injected) return injected

  const instance = getCurrentInstance() as { ce?: FormHost } | null
  return resolveHostFormEngine(instance?.ce ?? null)
}

/**
 * Resolve the parent Form's engine from a custom-element host via
 * `closest('tvu-form')`. Split out so a child can RETRY discovery in `onMounted`:
 * under `defineCustomElement` the parent Form's `setup` (which sets
 * `host._tvuFormEngine`) is NOT guaranteed to have run by the time the CHILD's
 * `setup` runs, so a setup-time read can miss the engine. By `onMounted` the
 * parent host property is reliably set (Stage 0 spike read it in onMounted). Pass
 * `getCurrentInstance().ce` (captured in setup) as `ce`.
 */
export function resolveHostFormEngine(ce: HTMLElement | null | undefined): FormEngine | null {
  if (!ce) return null
  const formHost = ce.closest('tvu-form') as FormHost | null
  return formHost?._tvuFormEngine ?? null
}

/**
 * INFRA-F120: the ambient parent Form's `disabled` state, CE-safely, for any
 * canonical control to inherit regardless of how it was placed (FormItem's
 * default slot fallback, or a consumer-supplied slot child). Must be called
 * synchronously in `setup()` — like `useFormEngine()`, it depends on
 * `getCurrentInstance()`/`inject()`, which only resolve correctly at that point;
 * calling it lazily inside a computed getter would silently return null.
 *
 * Unlike `useFormEngine()` (a one-shot lookup), this retries the CE host
 * discovery in `onMounted` — same reason FormItem.vue's own engine wiring does:
 * under `defineCustomElement` the parent Form's `setup` (which sets
 * `host._tvuFormEngine`) is not guaranteed to have run yet when a child's
 * `setup` runs.
 */
export function useFormDisabled(): ComputedRef<boolean> {
  const injected = inject(FormContextKey, null)
  const ownHost = (getCurrentInstance() as { ce?: FormHost } | null)?.ce ?? null
  const engine = ref<FormEngine | null>(injected)
  function tryDiscover() {
    if (engine.value) return
    engine.value = resolveHostFormEngine(ownHost)
  }
  tryDiscover()
  onMounted(tryDiscover)
  return computed(() => engine.value?.getDisabled() ?? false)
}
