/**
 * Wait until an element is actually visible in the viewport.
 *
 * The highlight overlay scrolls an off-screen target into view before
 * drawing its spotlight — but `scrollIntoView({ behavior: 'smooth' })`
 * settles asynchronously, so measuring the rect right after the call
 * yields a stale (mid-scroll or still off-screen) position. This waits
 * for the browser to confirm the element is on screen.
 */

/** Options for {@link waitForVisible}. */
export interface WaitForVisibleOptions {
  /**
   * Fraction of the element that must be inside the viewport before it
   * counts as visible (IntersectionObserver threshold).
   */
  threshold?: number;
  /**
   * Safety cap (ms). If the element never reaches the threshold — e.g.
   * it is larger than the viewport, or hidden — the promise resolves
   * anyway so the overlay never hangs. Fail soft.
   */
  timeoutMs?: number;
}

/**
 * Resolve once `element` is at least `threshold` visible in the
 * viewport, or once `timeoutMs` elapses — whichever comes first.
 *
 * Always resolves (never rejects): a never-intersecting element must
 * not stall the overlay. The boolean result reports whether the
 * element became visible (`true`) or the timeout fired (`false`), for
 * callers that want to log it.
 */
export function waitForVisible(
  element: Element,
  { threshold = 0.5, timeoutMs = 800 }: WaitForVisibleOptions = {},
): Promise<boolean> {
  return new Promise((resolve) => {
    // No IntersectionObserver (old/test env) — resolve immediately and
    // let the caller measure as before.
    if (typeof IntersectionObserver === 'undefined') {
      resolve(false);
      return;
    }

    let settled = false;
    const finish = (visible: boolean) => {
      if (settled) return;
      settled = true;
      window.clearTimeout(timer);
      observer.disconnect();
      resolve(visible);
    };

    const timer = window.setTimeout(() => finish(false), timeoutMs);
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting && entry.intersectionRatio >= threshold) {
            finish(true);
            return;
          }
        }
      },
      { threshold },
    );
    observer.observe(element);
  });
}
