import { expect, test } from '@playwright/test';

export interface PollOrSkipOptions {
  /** Maximum time to keep retrying `assertion` before giving up, in milliseconds. */
  timeout: number;
  /** Annotation recorded on the skipped test explaining why it was abandoned. */
  reason: string;
}

/**
 * Whether a timed-out poll should degrade to a skip, controlled by `SKIP_ON_BACKEND_TIMEOUT`.
 *
 * Env vars are strings, so a naive truthiness check would treat `'0'`/`'false'` as enabled (any
 * non-empty string is truthy). We parse explicitly instead: unset, empty, `'0'` and `'false'`
 * mean off; anything else (e.g. `'1'`, `'true'`) means on. So setting the var to `0` in the
 * pipeline turns skipping off as expected, without having to unset it.
 */
function skipOnBackendTimeoutEnabled(): boolean {
  const raw = process.env.SKIP_ON_BACKEND_TIMEOUT?.trim().toLowerCase();

  return raw !== undefined && raw !== '' && raw !== '0' && raw !== 'false';
}

/**
 * Polls `assertion` until it passes, exactly like `expect(assertion).toPass({ timeout })`.
 *
 * On timeout the behaviour depends on the environment:
 * - when `SKIP_ON_BACKEND_TIMEOUT` is enabled (the scheduled CI lane) the running test is marked
 *   **skipped** with `reason` instead of failing, so a backed-up backend job queue degrades to a
 *   skip rather than a red build;
 * - when it is off/unset (local/PR runs) the original timeout error is rethrown, so genuine
 *   regressions still surface while you debug.
 *
 * Only wrap polls whose sole realistic failure mode is backend throughput (async invoice
 * generation, Stripe settlement). Wrapping a poll that can settle on a *wrong* value would mask
 * that bug as a skip. See TODO(KIN-4692) — remove once test-env queue throughput is raised.
 */
export async function pollOrSkip(assertion: () => Promise<void> | void, options: PollOrSkipOptions): Promise<void> {
  try {
    await expect(assertion).toPass({ timeout: options.timeout });
  } catch (error) {
    if (!skipOnBackendTimeoutEnabled()) {
      throw error;
    }

    test.skip(true, options.reason);
  }
}
