/**
 * Shared helpers for trait tests.
 *
 * Each trait under packages/web-components/traits/ has a sibling .test.js
 * that imports from here. The universal contract (every trait must
 * register schema + clean up its attributes on disconnect) is asserted
 * once in `_smoke.test.js`; per-trait tests cover specific behaviors.
 */

import { expect } from 'vitest';

/**
 * Create a host element, append to document.body, return it.
 * Pass `attrs` to pre-set attributes before connecting the trait.
 */
export function mountHost(tag = 'div', attrs = {}) {
  const host = document.createElement(tag);
  for (const [k, v] of Object.entries(attrs)) host.setAttribute(k, String(v));
  document.body.appendChild(host);
  return host;
}

/**
 * Connect a trait to a host. Returns the trait instance for later disconnect.
 */
export function connectTrait(traitFactory, host, ctx) {
  const inst = traitFactory();
  inst.connect(host, ctx);
  return inst;
}

/**
 * Settle reactive effects (signals flush via queueMicrotask).
 */
export const tick = () => new Promise((r) => queueMicrotask(r));

/**
 * Wait for the next animation frame.
 */
export const raf = () => new Promise((r) => requestAnimationFrame(r));

/**
 * Wait `ms` milliseconds — for animation/timer-driven traits.
 */
export const wait = (ms) => new Promise((r) => setTimeout(r, ms));

/**
 * Spy an event — returns { detail, count } populated as the event fires.
 * Auto-disconnects when the host disconnects.
 */
export function spyEvent(host, eventName) {
  const captures = [];
  const handler = (e) => captures.push(e.detail);
  host.addEventListener(eventName, handler);
  return {
    get count() { return captures.length; },
    get last() { return captures[captures.length - 1]; },
    captures,
    cleanup: () => host.removeEventListener(eventName, handler),
  };
}

/**
 * Assert a trait schema has the contract fields populated.
 */
export function expectValidSchema(traitFactory) {
  const s = traitFactory.schema;
  expect(s).toBeDefined();
  expect(s.name).toBeTruthy();
  expect(s.category).toBeTruthy();
  expect(s.description).toBeTruthy();
  expect(Array.isArray(s.attributes)).toBe(true);
  expect(Array.isArray(s.events)).toBe(true);
  expect(Array.isArray(s.config)).toBe(true);
}

/**
 * Tear down the document body. Call in beforeEach.
 */
export function resetDOM() {
  document.body.innerHTML = '';
}
