// Drop-in replacement for snapdom/vitest.config.js.
// Adds snapdiff file-system commands to the existing browser config.
// BROWSER env var selects the engine: chromium (default) | firefox | webkit | all.

import { defineConfig, configDefaults } from 'vitest/config'
import { snapDiffCommands } from '@zumer/snapdiff/vitest'
import { createNetworkGate } from './vitest.network.mjs'

const ALL_BROWSERS = ['chromium', 'firefox', 'webkit']
const requested = process.env.BROWSER || 'chromium'
const browsers = requested === 'all' ? ALL_BROWSERS : [requested]

// Commands must be registered at the top browser level (per-instance commands
// are ignored by vitest), so each command resolves its baseDir from the
// project name (= browser) at call time to keep visual baselines separated.
const visualCommands = Object.fromEntries(
  Object.keys(snapDiffCommands()).map((name) => [name, (ctx, ...args) => {
    const browser = ctx.project?.name
    const baseDir = browser && browser !== 'chromium' ? `__snapshots__/visual-${browser}` : '__snapshots__/visual'
    return snapDiffCommands({ baseDir })[name](ctx, ...args)
  }])
)

// Answers "what can this connection serve right now?" for every browser worker, from the one
// node process they all share: run network-dependent tests as usual, run them one at a time, or
// skip them. See __tests__/helpers/network-gate.js.
// `workers` is what makes "fast enough" a question about this run rather than about the
// link: BROWSER=all puts three engines on the same connection, and it also sets what the
// serial lane may cost before the run gives up on network tests altogether.
const networkGate = createNetworkGate({ workers: browsers.length })

// A stylesheet the page cannot read: `cssRules` throws on a cross-origin sheet (verified on
// all three engines), which is what makes the author-style scan "unreliable" and switches
// every gate to its probe-everything path. The test page lives on localhost, so a second
// host is enough — the same machine on 127.0.0.1. Tests call
// `commands.serveCrossOriginCss(cssText)` and get a URL for a <link>.
// `commands.serveCrossOriginJpeg(repoPath)` serves a repo image from the same host with no
// CORS headers: it paints in an <img>, and snapdom's fetch of it fails.
const crossOrigin = (() => {
  const files = new Map()
  let server = null
  let port = 0
  async function start() {
    const { createServer } = await import('node:http')
    server = createServer((req, res) => {
      const file = files.get(req.url)
      if (!file) { res.writeHead(404); res.end(); return }
      res.writeHead(200, { 'content-type': file.type, 'cache-control': 'no-store' })
      res.end(file.body)
    })
    await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))
    server.unref() // must not keep the run alive after the last test
    port = server.address().port
  }
  async function serve(ext, type, body) {
    if (!server) await start()
    const path = `/${files.size}.${ext}`
    files.set(path, { type, body })
    return `http://127.0.0.1:${port}${path}`
  }
  return {
    commands: {
      serveCrossOriginCss: (_ctx, css) => serve('css', 'text/css', String(css)),
      async serveCrossOriginJpeg(_ctx, file) {
        const { readFile } = await import('node:fs/promises')
        return serve('jpg', 'image/jpeg', await readFile(new URL(file, import.meta.url)))
      },
    },
  }
})()

// The first reading is taken here, before any browser starts, and handed to the suites via
// inject('network'). Tests re-consult the gate as they run (the run itself is what congests the
// link), but a value known at COLLECTION time is what lets a suite size its own test timeout: a
// slow link needs a longer one, and vitest bakes timeouts in when the tests are registered. The
// probe is bounded and paid once per run.
const network = await networkGate.status()

export default defineConfig({
  // packages/plugins/* import '@zumer/snapdom' by NAME (they are published separately, so they
  // must). Under test that name resolved to whatever npm had installed in node_modules — the
  // last PUBLISHED release, 2.24.1 — so gif-export and video-export ran their internal
  // recaptures against v2 while claiming to test v3. Point the bare specifier at this
  // checkout's source: same module graph as `../src/...` imports in the tests, so the plugins
  // and the suite share one runtime and one plugin registry.
  resolve: {
    alias: { '@zumer/snapdom': new URL('./src/index.js', import.meta.url).pathname },
  },
  test: {
    // Scratch benchmarks can import aliases from their own config. Loading them here
    // raises Vite's error overlay over every test, corrupting screenshots and input.
    include: ['__tests__/**/*.test.js'],
    // An agent worktree checked out under .claude/worktrees/ carries its own __tests__/, and
    // the default glob ran both copies — a filter by file name matched twice.
    exclude: [...configDefaults.exclude, '**/.claude/**'],
    provide: {
      network: { mode: network.mode, reading: network.reading, kbps: network.kbps, serialTimeoutMs: networkGate.serialTimeoutMs },
      // Suites size their own timeouts from this: the same demo has three times the machine
      // and three times the link contention under BROWSER=all.
      engines: browsers.length,
      requireVisual: ['1', 'true', 'yes'].includes(String(process.env.REQUIRE_VISUAL || '').toLowerCase()),
    },
    // The visual suite tests compiled dist/ — never let it pixel-diff a stale build.
    globalSetup: ['./scripts/ensure-fresh-dist.mjs', './scripts/require-visual.mjs'],
    browser: {
      enabled: true,
      provider: 'playwright',
      screenshotFailures: false,
      instances: browsers.map((browser) => ({ browser })),
      commands: { ...visualCommands, ...networkGate.commands, ...crossOrigin.commands },
    },
    coverage: {
      provider: 'v8',
      include: ['src/**/*.js'],
    },
  },
})