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

/**
 * EventSnap E2E config.
 *
 * Defaults assume the test stack is already up (`npm run stack:up`). The
 * stack is *not* started by webServer because Playwright would clobber the
 * compose volumes between retries. CI calls `npm run stack:up` once before
 * `npm run test:e2e` runs.
 *
 * UA matrix: the `@smoke` happy-path runs across every project to catch
 * engine-level divergences. The rest of the suite only runs against
 * `chromium-desktop` to keep the wall-clock reasonable.
 */
// camera/microphone/clipboard are Chromium-only permissions; passing them to
// firefox/webkit projects throws "Unknown permission: camera" and fails the
// whole test before it runs. Granted per-Chromium-project below instead of in
// the global `use` block.
const CHROMIUM_PERMISSIONS = ['camera', 'microphone', 'clipboard-read', 'clipboard-write'];

export default defineConfig({
  testDir: './specs',
  outputDir: './test-results',
  fullyParallel: false, // Single shared backend → tests TRUNCATE between, so don't run in parallel.
  forbidOnly: !!process.env.CI,
  // NO RETRIES — not even in CI. This is deliberate and it is the opposite of the usual advice.
  //
  // The standard case for retries is "the environment is flaky, the product isn't." That argument
  // does not hold here. This backend's real bugs ARE races (the last several fixes were all export
  // concurrency), and from the outside a race is indistinguishable from a flake — so a retry
  // resolves that ambiguity, silently, in favour of "flake", every single time.
  //
  // The numbers make it concrete. A test that fails 3% of runs reports a bug roughly 1 run in 33.
  // Under `retries: 2` it fails the build only when it fails three times in a row: ~1 in 37,000.
  // We had exactly such a test, and it was reporting a REAL user-facing bug (a suggestion button
  // that committed on mousedown and destroyed itself mid-click). Retries would have buried it
  // forever, and buried it GREEN, so nobody would even have seen an amber.
  //
  // Worse, a retry does not re-run the failing conditions — it runs a CLEANER environment (the
  // interfering background worker from the previous test has since finished). So the mechanism is
  // specifically good at hiding exactly the cross-test contamination this suite is prone to.
  //
  // A flake here is a bug report. Treat it as one.
  retries: 0,
  workers: 1, // One worker. Multi-worker needs per-worker isolated DBs (Phase 2+).
  reporter: [['list'], ['html', { open: 'never', outputFolder: './playwright-report' }]],
  globalSetup: './global-setup.ts',
  globalTeardown: './global-teardown.ts',
  timeout: 60_000,
  expect: { timeout: 10_000 },

  use: {
    baseURL: process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    actionTimeout: 10_000,
    navigationTimeout: 30_000,
    // No camera/mic/clipboard here — those are Chromium-only and are granted on
    // the Chromium projects below (see CHROMIUM_PERMISSIONS).
  },

  projects: [
    // ── Baseline desktop ─────────────────────────────────────────────────
    {
      name: 'chromium-desktop',
      // 09-mobile/ specs only run on the chromium-mobile project below — they
      // require `hasTouch: true` and a phone viewport.
      testIgnore: ['**/09-mobile/**'],
      use: {
        ...devices['Desktop Chrome'],
        permissions: CHROMIUM_PERMISSIONS,
        launchOptions: {
          args: [
            '--use-fake-ui-for-media-stream',
            '--use-fake-device-for-media-stream',
            // A video file would be loaded with --use-file-for-fake-video-capture
            // — we ship a tiny .y4m in fixtures/media/ as a follow-up.
          ],
        },
      },
    },

    // ── Phase 3 mobile gestures ──────────────────────────────────────────
    // Runs ONLY the 09-mobile/ specs. Uses the Pixel 7 device descriptor so
    // hasTouch + isMobile + mobile viewport are all set, then dispatches
    // pointer events via page.mouse / locator helpers. Real touch events
    // come from `page.touchscreen` when needed.
    {
      name: 'chromium-mobile',
      testMatch: ['**/09-mobile/**/*.spec.ts'],
      use: { ...devices['Pixel 7'], permissions: CHROMIUM_PERMISSIONS },
    },

    // ── Mobile UA smoke matrix (runs only @smoke specs in CI) ────────────
    {
      name: 'chromium-pixel7',
      use: { ...devices['Pixel 7'], permissions: CHROMIUM_PERMISSIONS },
      grep: /@smoke/,
    },
    {
      name: 'chromium-galaxy-s22',
      use: {
        ...devices['Galaxy S9+'], // Playwright doesn't ship S22 yet — S9+ is the closest Samsung descriptor.
        viewport: { width: 360, height: 780 },
        userAgent:
          'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36',
        permissions: CHROMIUM_PERMISSIONS,
      },
      grep: /@smoke/,
    },
    {
      name: 'samsung-internet',
      use: {
        ...devices['Galaxy S9+'],
        viewport: { width: 360, height: 780 },
        userAgent:
          'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/124.0.0.0 Mobile Safari/537.36',
        permissions: CHROMIUM_PERMISSIONS,
      },
      grep: /@smoke/,
    },
    {
      name: 'edge-android',
      use: {
        ...devices['Pixel 7'],
        userAgent:
          'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.0.0',
        permissions: CHROMIUM_PERMISSIONS,
      },
      grep: /@smoke/,
    },
    {
      name: 'chrome-ios',
      use: {
        ...devices['iPhone 14 Pro'],
        userAgent:
          'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/124.0.0.0 Mobile/15E148 Safari/604.1',
      },
      grep: /@smoke/,
    },
    {
      name: 'webkit-iphone',
      use: { ...devices['iPhone 14 Pro'] },
      // The PRIMARY user of this app is a wedding guest opening a QR link in iOS Safari. Gating
      // that on `@smoke` — which exists on exactly two specs — meant the entire iOS guarantee was
      // one happy path and one join test. Every other UA here is a secondary browser and a smoke
      // check is proportionate; WebKit is not. Give it the core journeys the guest actually walks:
      // join/recover, upload, browse the feed — and take the keepsake home.
      //
      // 06-export is here because WebKit is the ONLY engine that enforces X-Frame-Options on the
      // hidden download iframe. Excluding it is what let a site-wide `XFO: DENY` ship a keepsake
      // download that silently did nothing on iOS. See 06-export/download-iframe.spec.ts.
      testMatch: [
        '**/__smoke/**',
        '**/01-auth/**',
        '**/02-upload/**',
        '**/03-feed/**',
        '**/06-export/**',
      ],
    },
    {
      name: 'firefox-android',
      use: {
        ...devices['Pixel 7'],
        defaultBrowserType: 'firefox',
        // Firefox rejects `isMobile` (Chromium-only). Keep the phone viewport +
        // Android UA for coverage, but drop the unsupported flag.
        isMobile: false,
        userAgent: 'Mozilla/5.0 (Android 14; Mobile; rv:124.0) Gecko/124.0 Firefox/124.0',
      },
      grep: /@smoke/,
    },
    {
      name: 'firefox-desktop',
      use: { ...devices['Desktop Firefox'] },
      grep: /@smoke/,
    },
  ],
});
