import { readdirSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { defineConfig } from "vitest/config";

// Resolve api-workspace packages that pnpm only exposes under apps/api/node_modules.
const apiNodeModules = path.resolve(__dirname, "apps/api/node_modules");

// Resolve web-workspace packages that pnpm only exposes under apps/web/node_modules.
const webNodeModules = path.resolve(__dirname, "apps/web/node_modules");

// Resolve landing-workspace packages.
const landingNodeModules = path.resolve(__dirname, "apps/landing/node_modules");

// @opentelemetry/core is a transitive dep (via sdk-node) not hoisted by pnpm.
// Find it in the pnpm store so tests can import W3CTraceContextPropagator.
function findPnpmPackage(scope: string, name: string): string {
  const pnpmDir = path.resolve(__dirname, "node_modules/.pnpm");
  const prefix = `${scope}+${name}@`;
  const entries = readdirSync(pnpmDir)
    .filter((e) => e.startsWith(prefix))
    .sort();
  if (entries.length === 0) {
    throw new Error(`${scope}/${name} not found in pnpm store`);
  }
  return path.join(pnpmDir, entries[entries.length - 1], "node_modules", scope, name);
}

export default defineConfig({
  esbuild: {
    jsx: "automatic",
  },
  test: {
    globals: true,
    // Env-overridable so the resource-constrained docker test image (where Sharp
    // and FFmpeg run ~2-3x slower under the macOS Docker VM) can grant slow
    // sync-wait jobs more headroom. Host/CI keep the 30s default.
    testTimeout: Number(process.env.VITEST_TEST_TIMEOUT) || 30_000,
    hookTimeout: Number(process.env.VITEST_HOOK_TIMEOUT) || 30_000,
    pool: "forks",
    poolOptions: {
      forks: {
        // Parallel forks; each fork gets an isolated DB + workspace via
        // tests/setup/per-fork-env.ts. CI runners have 4 vCPUs. Env-overridable
        // (VITEST_MAX_FORKS) so the heavy nightly jobs (FULL_MATRIX, coverage)
        // can drop to fewer forks and give each slow media test more CPU, which
        // keeps format-matrix conversions from starving and timing out.
        maxForks:
          Number(process.env.VITEST_MAX_FORKS) ||
          (process.env.CI ? 4 : Math.max(2, Math.floor(os.availableParallelism() / 2))),
      },
    },
    globalSetup: ["tests/global-setup.ts"],
    setupFiles: ["tests/setup/per-fork-env.ts"],
    exclude: [
      "tests/e2e/**",
      "tests/e2e-docs/**",
      "tests/e2e-editor/**",
      "tests/e2e-landing/**",
      "tests/e2e-docker/**",
      "tests/e2e-analytics/**",
      // tests/qa/* are Playwright QA-sweep specs (and .mts harnesses), not
      // vitest tests; they import @playwright/test and break a bare `vitest run`.
      "tests/qa/**",
      "**/node_modules/**",
      "**/dist/**",
      "**/.{idea,git,cache,output,temp}/**",
      ".worktrees/**",
      ".claude/**",
    ],
    env: {
      AUTH_ENABLED: "true",
      DEFAULT_USERNAME: "admin",
      DEFAULT_PASSWORD: "Adminpass1",
      // DATABASE_URL and WORKSPACE_PATH are set per-fork in tests/setup/per-fork-env.ts
      MAX_UPLOAD_SIZE_MB: "10",
      MAX_BATCH_SIZE: "10",
      RATE_LIMIT_PER_MIN: "10000",
      MAX_USERS: "0",
      MAX_MEGAPIXELS: "100",
      CONCURRENT_JOBS: "3",
      FILE_MAX_AGE_HOURS: "1",
      CLEANUP_INTERVAL_MINUTES: "60",
      MAX_PIPELINE_STEPS: "0",
    },
    coverage: {
      provider: "v8",
      reporter: ["text", "html", "lcov"],
      // Ratchet: measured 2026-06-10 at lines 77.7 / branches 83.7 /
      // functions 86.5 over unit+integration. Raise when coverage rises;
      // never lower without a written reason.
      // 2026-06-24: re-baselined branches 81 -> 80 and functions 84 -> 83.
      // The 2.0 surface growth (157 tools) plus the analytics-system removal
      // (#336 deleted tested code) settled the all-tests-passing measurement at
      // branches 80.97 / functions 83.36, just under the old ratchet. Per-tool
      // integration tests still cover the critical paths.
      thresholds: {
        lines: 75,
        branches: 80,
        functions: 83,
        statements: 75,
      },
      include: [
        "packages/image-engine/src/**",
        "apps/api/src/**",
        "apps/web/src/stores/**",
        "apps/web/src/lib/**",
      ],
      exclude: [
        "**/*.d.ts",
        "**/node_modules/**",
        "**/dist/**",
        "apps/api/src/db/migrate.ts",
        "apps/api/src/index.ts",
      ],
    },
  },
  resolve: {
    alias: {
      "@landing": path.resolve(__dirname, "apps/landing/src"),
      // Landing page components that don't exist in web but are imported via @/
      "@/components/fade-in": path.resolve(__dirname, "apps/landing/src/components/fade-in"),
      "@/components/footer": path.resolve(__dirname, "apps/landing/src/components/footer"),
      "@/components/json-ld": path.resolve(__dirname, "apps/landing/src/components/json-ld"),
      "@/components/navbar": path.resolve(__dirname, "apps/landing/src/components/navbar"),
      "@": path.resolve(__dirname, "apps/web/src"),
      "framer-motion": path.join(landingNodeModules, "framer-motion"),
      "@snapotter/ai": path.resolve(__dirname, "packages/ai/src/index.ts"),
      "@snapotter/enterprise": path.resolve(__dirname, "packages/enterprise/src/index.ts"),
      "@snapotter/image-engine": path.resolve(__dirname, "packages/image-engine/src/index.ts"),
      "@snapotter/media-engine": path.resolve(__dirname, "packages/media-engine/src/index.ts"),
      "@snapotter/doc-engine": path.resolve(__dirname, "packages/doc-engine/src/index.ts"),
      "@snapotter/shared/i18n": path.resolve(__dirname, "packages/shared/src/i18n"),
      "@snapotter/shared": path.resolve(__dirname, "packages/shared/src/index.ts"),
      fastify: path.join(apiNodeModules, "fastify"),
      "@fastify/cookie": path.join(apiNodeModules, "@fastify/cookie"),
      "@fastify/cors": path.join(apiNodeModules, "@fastify/cors"),
      "@fastify/multipart": path.join(apiNodeModules, "@fastify/multipart"),
      "@fastify/rate-limit": path.join(apiNodeModules, "@fastify/rate-limit"),
      "@fastify/static": path.join(apiNodeModules, "@fastify/static"),
      "@fastify/swagger": path.join(apiNodeModules, "@fastify/swagger"),
      "@fastify/swagger-ui": path.join(apiNodeModules, "@fastify/swagger-ui"),
      "better-sqlite3": path.join(apiNodeModules, "better-sqlite3"),
      pg: path.join(apiNodeModules, "pg"),
      "drizzle-orm": path.join(apiNodeModules, "drizzle-orm"),
      archiver: path.join(apiNodeModules, "archiver"),
      "p-queue": path.join(apiNodeModules, "p-queue"),
      dotenv: path.join(apiNodeModules, "dotenv"),
      potrace: path.join(apiNodeModules, "potrace"),
      qrcode: path.join(apiNodeModules, "qrcode"),
      jsqr: path.join(apiNodeModules, "jsqr"),
      pdfkit: path.join(apiNodeModules, "pdfkit"),
      pino: path.join(apiNodeModules, "pino"),
      sharp: path.join(apiNodeModules, "sharp"),
      ioredis: path.join(apiNodeModules, "ioredis"),
      bullmq: path.join(apiNodeModules, "bullmq"),
      otpauth: path.join(apiNodeModules, "otpauth"),
      "openid-client": path.join(apiNodeModules, "openid-client"),
      "opentype.js": path.join(apiNodeModules, "opentype.js"),
      "posthog-node": path.join(apiNodeModules, "posthog-node"),
      "@sentry/node": path.join(apiNodeModules, "@sentry/node"),
      "@aws-sdk/client-s3": path.join(
        path.resolve(__dirname, "packages/enterprise/node_modules"),
        "@aws-sdk/client-s3",
      ),
      react: path.join(webNodeModules, "react"),
      "react-dom": path.join(webNodeModules, "react-dom"),
      "react-router-dom": path.join(webNodeModules, "react-router-dom"),
      zustand: path.join(webNodeModules, "zustand"),
      "posthog-js": path.join(webNodeModules, "posthog-js"),
      "@sentry/react": path.join(webNodeModules, "@sentry/react"),
      "@opentelemetry/api": path.join(apiNodeModules, "@opentelemetry/api"),
      "@opentelemetry/core": findPnpmPackage("@opentelemetry", "core"),
      "@opentelemetry/sdk-node": path.join(apiNodeModules, "@opentelemetry/sdk-node"),
      "@opentelemetry/sdk-trace-base": path.join(apiNodeModules, "@opentelemetry/sdk-trace-base"),
      "@opentelemetry/exporter-trace-otlp-http": path.join(
        apiNodeModules,
        "@opentelemetry/exporter-trace-otlp-http",
      ),
      "@opentelemetry/resources": path.join(apiNodeModules, "@opentelemetry/resources"),
      "@opentelemetry/semantic-conventions": path.join(
        apiNodeModules,
        "@opentelemetry/semantic-conventions",
      ),
      "@opentelemetry/instrumentation-http": path.join(
        apiNodeModules,
        "@opentelemetry/instrumentation-http",
      ),
      "@opentelemetry/instrumentation-fastify": path.join(
        apiNodeModules,
        "@opentelemetry/instrumentation-fastify",
      ),
      "@opentelemetry/instrumentation-pg": path.join(
        apiNodeModules,
        "@opentelemetry/instrumentation-pg",
      ),
      "@opentelemetry/instrumentation-ioredis": path.join(
        apiNodeModules,
        "@opentelemetry/instrumentation-ioredis",
      ),
      "@opentelemetry/instrumentation-aws-sdk": path.join(
        apiNodeModules,
        "@opentelemetry/instrumentation-aws-sdk",
      ),
    },
  },
});
