// INFRA-F54 regression guard — CE named-slot projection in the React /
// web-component build.
//
// Root cause (Vue 3.5 runtime-dom): the custom-element root vnode is created
// with NO slot children, so a canonical gating a named slot with `v-if="$slots.x"`
// (or a useSlots-derived guard) drops that slot ONLY in the CE build — the plain
// Vue SFC build and vitest stay green, so the bug hides. The fix is the CE-safe
// `useHasSlot()` guard (src/canonical/composables/useHasSlot.ts) which detects the
// real host slot (querySelector for shadow-DOM CEs, host._slots for light-DOM CEs)
// and forwards the outlet ONLY when slotted content actually exists — otherwise the
// base's prop / native-slot fallback must still render.
//
// This is the blind spot the pre-existing render-verification-react suite had:
// popupbox-slot-projection.spec.ts covered only DEFAULT-slot projection. Named
// slots (FormItem label / Tooltip content / PopupBox footer) were unguarded.
//
// Ground truth = element VISIBILITY, not naive textContent. A shadow-DOM host
// keeps unprojected light-DOM `[slot=x]` children in its light DOM, so reading
// textContent would be fooled by that residual. Chromium's checkVisibility()
// returns false for unslotted light children (they have no layout box), so we
// count text only from actually-rendered elements, piercing open shadow roots.
//
// Guard: vitest also globs *.spec.ts — skip there so pre-commit `vitest run`
// doesn't execute Playwright test() in jsdom. Runs via
// playwright.render-verification-react.config.ts.
if (process.env.VITEST) {
  const { test } = await import('vitest')
  test.skip('INFRA-F54 named-slot projection runs via playwright.render-verification-react.config.ts', () => {})
} else {
  const { test, expect } = await import('@playwright/test')

  // Collect visible text across the document and all open shadow roots, counting a
  // text node only when its parent element is actually rendered. Serialized into
  // page.evaluate — keep it self-contained (no external refs).
  const collectVisibleText = () => {
    const out: string[] = []
    const walk = (root: Document | ShadowRoot) => {
      for (const el of Array.from(root.querySelectorAll('*'))) {
        if (el.shadowRoot) walk(el.shadowRoot)
        for (const n of Array.from(el.childNodes)) {
          if (n.nodeType === 3 && (n.textContent ?? '').trim()) {
            const vis = typeof (el as any).checkVisibility === 'function'
              ? (el as any).checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })
              : true
            if (vis) out.push((n.textContent ?? '').trim())
          }
        }
      }
    }
    walk(document)
    return out.join(' | ')
  }

  // Each case: a `-slot` variant (named slot present → slotted content wins over
  // the prop/native fallback) and a `-noslot` variant (no slot → prop/native
  // fallback must still render in the CE build). Covers both shadow-DOM CEs
  // (FormItem, Tooltip) and the light-DOM CE (PopupBox).
  const CASES = [
    { f54: 'formitem-slot',   present: 'FI_SLOT_LABEL',   absent: 'FI_PROP_LABEL' },
    { f54: 'formitem-noslot', present: 'FI_PROP_LABEL',   absent: 'FI_SLOT_LABEL' },
    { f54: 'tooltip-slot',    present: 'TT_SLOT_CONTENT', absent: 'TT_PROP_CONTENT' },
    { f54: 'tooltip-noslot',  present: 'TT_PROP_CONTENT', absent: 'TT_SLOT_CONTENT' },
    { f54: 'popupbox-slot',   present: 'PB_SLOT_FOOTER',  absent: 'Cancel' },
    { f54: 'popupbox-noslot', present: 'Confirm',         absent: 'PB_SLOT_FOOTER' },
  ]

  for (const c of CASES) {
    test(`INFRA-F54 named-slot projection: ${c.f54}`, async ({ page }) => {
      await page.goto(`/?f54=${c.f54}&theme=dark`)
      // Wait for the wrapper's useEffect prop push + CE mount to settle. Use
      // 'attached' not 'visible': PopupBox teleports its modal to <body>, leaving
      // the wrapper div itself empty/hidden.
      await page.locator(`[data-f54-case="${c.f54}"]`).waitFor({ state: 'attached' })
      await page.waitForTimeout(200)

      const visibleText = await page.evaluate(collectVisibleText)

      expect(
        visibleText.includes(c.present),
        `expected "${c.present}" to be visibly rendered — got: ${visibleText}`,
      ).toBe(true)
      expect(
        visibleText.includes(c.absent),
        `expected "${c.absent}" NOT to be rendered (fallback/slot competition) — got: ${visibleText}`,
      ).toBe(false)
    })
  }
}
