// Playwright config for the post-PR-17 `Preview smoke` and
// `Screenshot diff` workflows. ESM per the repo's
// `"type": "module"` setting in package.json. Companion docs:
// `.convoys/adopt-playwright-smoke.md` (Decisions 1-6),
// AGENTS.md § 7 (Vercel preview bypass conventions).

import { defineConfig } from '@playwright/test';

const BASE_URL = process.env.BASE_URL ?? 'http://localhost:3000';
const BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;

// `CI === 'true'` is the canonical GitHub Actions signal (set by
// the runner). Playwright config has no Next.js context, so
// `NODE_ENV` is not reliable here. Mirrors the rate-limit.js
// fail-closed pattern documented in AGENTS.md Gotcha #12.
const IS_CI = process.env.CI === 'true';

if (IS_CI && !BYPASS_SECRET) {
  // Fail loud in CI per Decision 2. The workflow's `env:` block
  // (preview-smoke.yml line 101, visual-diff.yml line 85) maps
  // `secrets.VERCEL_AUTOMATION_BYPASS_SECRET` into the process
  // env; if it's empty here, the secret is unseeded or the
  // workflow YAML drift broke the mapping.
  throw new Error(
    'VERCEL_AUTOMATION_BYPASS_SECRET is required in CI to reach ' +
    'Vercel-Protection-protected preview deployments. ' +
    'Reseed via: gh secret set VERCEL_AUTOMATION_BYPASS_SECRET --body "<value>". ' +
    'See AGENTS.md § 7 for the full plumbing context.'
  );
}

if (!BYPASS_SECRET && !IS_CI) {
  // Dev fallback per Decision 2 — warn once at config load,
  // proceed without the header. Local runs target localhost
  // (no preview protection) or a non-protected URL.
  // (No eslint-disable for `no-console` here: the active config
  // does not enable `no-console` for repo-root config files;
  // adding the directive here trips `eslint-comments/no-unused-disable`.)
  console.warn(
    '[playwright.config] VERCEL_AUTOMATION_BYPASS_SECRET unset — ' +
    'running without the Vercel bypass header. Targets a non-protected ' +
    'URL (e.g. http://localhost:3000). Hitting a protected preview without ' +
    'this header will return Vercel\'s SSO challenge page.'
  );
}

export default defineConfig({
  // Both `tests/smoke/` and `tests/visual/` live under `tests/`.
  // Project-level `testMatch` (below) partitions them so the
  // two workflows (`--project=smoke` and `--project=visual`)
  // each see only the specs they should run.
  testDir: './tests',

  // Smoke + visual specs are independent; parallelism within a
  // single spec adds no value here and would complicate the
  // per-test screenshot baseline lifecycle.
  fullyParallel: false,
  workers: IS_CI ? 1 : undefined,

  // One retry in CI handles transient Vercel preview flakes
  // (cold-start, DNS propagation). Local: zero retries — fail
  // fast so the dev sees the issue immediately.
  retries: IS_CI ? 1 : 0,

  // 30s per test is plenty for the 3 smoke checks + 1 visual
  // screenshot. The convoy file's success metric is < 5min
  // total workflow runtime; per-test 30s is well inside that.
  timeout: 30_000,
  expect: { timeout: 10_000 },

  // List reporter in dev for human readability; add HTML in CI
  // so the `Upload Playwright report on failure` step
  // (preview-smoke.yml line 104) has a populated `playwright-report/`
  // to upload. `open: 'never'` keeps the HTML from auto-launching
  // a browser tab in headless CI.
  reporter: IS_CI ? [['list'], ['html', { open: 'never' }]] : 'list',

  // Visual baselines live at `tests/visual/__screenshots__/<arg>{ext}`.
  // Workflow `visual-diff.yml` uploads this exact path as the artifact —
  // keep them aligned. {arg} is the snapshot name from
  // `toHaveScreenshot('home.png')` without the extension; {ext} is
  // the extension with the leading dot.
  //
  // Cross-platform CAUTION (still live): this template drops Playwright's
  // default `-<browser>-<platform>` suffix. A Mac dev running
  // `npm run test:visual:update` silently overwrites the Linux-CI
  // baseline with a Mac-rendered PNG that the CT 111 runner will reject
  // on the next diff. The seeded Linux baseline (PR #58 `83a358b`,
  // 2026-06-02) is the canonical artifact. Re-seed via the Linux Docker
  // recipe in `tests/visual/homepage.spec.ts`'s module docblock OR
  // dispatch the (queued) `seed-visual-baselines` workflow on CT 111.
  // Never via `npm run test:visual:update` on a Mac.
  snapshotPathTemplate: 'tests/visual/__screenshots__/{arg}{ext}',

  use: {
    baseURL: BASE_URL,
    // Headers apply to BOTH browser `page.goto(...)` calls AND the
    // test-level `request` fixture's APIRequestContext (verified
    // against Playwright docs: `testOptions.extraHTTPHeaders` is
    // shared between browser context and APIRequestContext
    // construction). This is why `tests/smoke/app.smoke.spec.ts`'s
    // third test (`request.get('/api/health')`) reaches the
    // protected preview without re-injecting the header in the
    // spec body. If a future hotfix shows the header NOT
    // propagating to APIRequestContext, see Risk R1 in the
    // convoy file's Architecture section.
    extraHTTPHeaders: BYPASS_SECRET
      ? { 'x-vercel-protection-bypass': BYPASS_SECRET }
      : undefined,
    // Trace OFF this convoy. Enabling it would land the bypass
    // header in the HAR payload (Risk R6); a future polish convoy
    // owns the trace-on + HAR-sanitization decision.
    trace: 'off',
    screenshot: 'off',
    video: 'off',
  },

  projects: [
    {
      // `Preview smoke` workflow invokes `--project=smoke` (per
      // preview-smoke.yml line 98). The testMatch keeps the
      // visual specs out of this project.
      name: 'smoke',
      testMatch: 'smoke/**/*.spec.@(ts|js)',
    },
    {
      // `Screenshot diff` workflow invokes `--project=visual`
      // (per visual-diff.yml line 82). The testMatch keeps the
      // smoke specs out of this project (so a `--project=visual`
      // run doesn't redundantly execute the smoke tests).
      name: 'visual',
      testMatch: 'visual/**/*.spec.@(ts|js)',
    },
  ],
});
