// @ts-check
import { defineConfig, devices } from '@playwright/test';
import { defineCoverageReporterConfig } from '@bgotink/playwright-coverage';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

/**
 * Read environment variables from file (local development only).
 * In CI, environment variables are provided by GitHub secrets.
 * https://github.com/motdotla/dotenv
 */
import dotenv from 'dotenv';
if (!process.env.CI) {
  dotenv.config({ path: join(dirname(fileURLToPath(import.meta.url)), '.env') });
}

/**
 * Auth state storage path - shared across all browser projects
 */
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const STORAGE_STATE = join(__dirname, 'playwright/.auth/user.json');

/**
 * Coverage reporter configuration for E2E tests
 * Disabled by default; enable with PLAYWRIGHT_COVERAGE=1.
 */
const enableCoverage = process.env.PLAYWRIGHT_COVERAGE === '1';
const resolvedBaseURL = process.env.PLAYWRIGHT_BASE_URL || (enableCoverage ? 'http://localhost:5173' : 'http://127.0.0.1:8080');
if (!process.env.PLAYWRIGHT_BASE_URL) {
  process.env.PLAYWRIGHT_BASE_URL = resolvedBaseURL;
}
// Skip security-test dependencies by default to avoid running the security
// shard setup/teardown as a prerequisite for non-security test runs.
// Set PLAYWRIGHT_SKIP_SECURITY_DEPS=0 to restore legacy dependency behavior.
const skipSecurityDeps = process.env.PLAYWRIGHT_SKIP_SECURITY_DEPS !== '0';
const browserDependencies = skipSecurityDeps ? ['setup'] : ['setup', 'security-tests'];

const coverageReporterConfig = enableCoverage ? defineCoverageReporterConfig({
  sourceRoot: __dirname,
  exclude: [
    '**/node_modules/**',
    '**/playwright/**',
    '**/tests/**',
    '**/*.spec.ts',
    '**/*.spec.js',
    '**/*.test.ts',
    '**/coverage/**',
    '**/dist/**',
    '**/build/**',
  ],
  resultDir: join(__dirname, 'coverage/e2e'),
  reports: [
    ['html'],
    ['lcovonly', { file: 'lcov.info' }],
    ['json', { file: 'coverage.json' }],
    ['text-summary', { file: null }],
  ],
  watermarks: {
    statements: [50, 80],
    branches: [50, 80],
    functions: [50, 80],
    lines: [50, 80],
  },
  rewritePath: ({ absolutePath }) => {
    if (absolutePath.startsWith('/app/')) {
      return absolutePath.replace('/app/', `${__dirname}/`);
    }
    if (absolutePath.startsWith('/src/')) {
      return join(__dirname, 'frontend', absolutePath);
    }
    if (!absolutePath.startsWith('/') && !absolutePath.includes('/')) {
      return join(__dirname, 'frontend/src', absolutePath);
    }
    return absolutePath;
  },
}) : null;

/**
 * @see https://playwright.dev/docs/test-configuration
 */

// Preflight: when the Playwright UI is requested on a headless Linux machine,
// attempt to start an Xvfb instance automatically (developer convenience).
// - If Xvfb is not available, fail with a clear, actionable message.
// - In CI we avoid auto-starting; CI should either use the project's E2E Docker
//   image or run tests in headless mode.
if (process.argv.includes('--ui')) {
  if (process.env.CI) {
    // In CI, running the interactive UI is unsupported — provide guidance.
    throw new Error(
      "Playwright UI (--ui) is not supported in CI.\n" +
      "Use the project's E2E Docker image or run tests headless: `npm run e2e`"
    );
  }

  if (!process.env.DISPLAY) {
    try {
      // Use child_process to probe for Xvfb and start it if present.
      const { spawnSync, spawn } = await import('child_process');
      const probe = spawnSync('Xvfb', ['-version']);
      if (probe.error) throw probe.error;

      // Start Xvfb on :99 and detach so it survives after the spawn call.
      const xvfb = spawn('Xvfb', [':99', '-screen', '0', '1280x720x24'], {
        detached: true,
        stdio: 'ignore',
      });
      xvfb.unref();
      process.env.DISPLAY = ':99';
      // eslint-disable-next-line no-console
      console.log('Started Xvfb on :99 to support Playwright UI (auto-start).');
    } catch (err) {
      throw new Error(
        'Playwright UI requires an X server but none was found.\n' +
        "Options:\n" +
        "  1) Install Xvfb and retry (Debian/Ubuntu: `sudo apt install xvfb`)\n" +
        "  2) Run the UI under Xvfb: `xvfb-run --auto-servernum npx playwright test --ui`\n" +
        "  3) Run headless tests: `npm run e2e`\n\n" +
        "See docs/development/running-e2e.md for details.\n" +
        `Underlying error: ${err && err.message ? err.message : err}`
      );
    }
  }
}

export default defineConfig({
  testDir: './tests',
  testIgnore: ['**/frontend/**', '**/node_modules/**', '**/backend/**'],

  /* Standard globalSetup - runs once before all tests */
  globalSetup: './tests/global-setup.ts',

  /* Timeouts */
  timeout: process.env.CI ? 60000 : 90000,
  expect: { timeout: 5000 },

  /* Parallelization */
  fullyParallel: true,
  workers: process.env.CI ? 1 : undefined,

  /* CI settings */
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,

  /* Reporters - simplified for CI */
  reporter: [
    process.env.CI ? ['github'] : ['list'],
    ['html', { open: process.env.CI ? 'never' : 'on-failure' }],
    ...(enableCoverage ? [['@bgotink/playwright-coverage', coverageReporterConfig]] : []),
  ],
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
  use: {
    /* Base URL Configuration
     *
     * Coverage mode (enableCoverage=true):
     *   - Vite dev server automatically started via webServer block
     *   - Uses http://localhost:5173 (source maps for V8 coverage)
     *   - PLAYWRIGHT_BASE_URL override still respected
     *
    * Non-coverage mode (enableCoverage=false):
     *   - Tests run against Docker container (faster, no coverage)
    *   - Uses http://127.0.0.1:8080 (IPv4 loopback to avoid ::1 connection flakiness)
     *   - PLAYWRIGHT_BASE_URL override still respected
     *
     * CRITICAL: Authentication cookies are domain-scoped. The auth.setup.ts
     * stores cookies for the domain in this baseURL. TestDataManager and
     * browser tests must use the SAME domain for cookies to be sent.
     *
    * Cookie domain note:
    * Cookies are domain-scoped. Auth setup and browser/API contexts must use
    * the same resolved base URL host.
     *
     * E2E tests verify UI/UX on the Charon management interface.
     * Middleware enforcement is tested separately via integration tests (backend/integration/).
     *
     * For remote SSH development, use PLAYWRIGHT_BASE_URL with your Tailscale IP:
     *   export PLAYWRIGHT_BASE_URL=http://100.98.12.109:9323
     *   npx playwright test --ui
     */
    baseURL: resolvedBaseURL,

    /* Traces: Capture execution traces for debugging
     *
     * Options:
     *   'off'              - No trace capture
     *   'on'               - Always capture (large files, use only for debugging)
     *   'on-first-retry'   - Capture on first retry only (good balance)
     *   'retain-on-failure'- Capture only for failed tests (smallest overhead)
     */
    trace: 'on-first-retry',

    /* Videos: Capture video recordings for visual debugging
     *
     * Options:
     *   'off'              - No recording
     *   'on'               - Always record (high disk usage)
     *   'retain-on-failure'- Record only failed tests (recommended)
     */
    video: 'retain-on-failure',

    /* Screenshots: Capture screenshots of page state
     *
     * Options:
     *   'off'              - No screenshots
     *   'only-on-failure'  - Screenshot on failure (recommended)
     *   'on'               - Always screenshot (high disk usage)
     */
    screenshot: 'only-on-failure',
  },

  /* Run your local dev server before starting the tests */
  webServer: enableCoverage ? {
    command: 'cd frontend && npm run dev',
    url: 'http://localhost:5173',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
    stdout: 'pipe',
    stderr: 'pipe',
  } : undefined,

  /* Configure projects for major browsers */
  projects: [
    // Setup project - authentication (runs FIRST)
    {
      name: 'setup',
      testMatch: /auth\.setup\.ts/,
    },

    // Security Shard Setup - runs only when security-tests are executed
    {
      name: 'security-shard-setup',
      testMatch: /security-shard\.setup\.ts/,
      dependencies: ['setup'],
    },

    // Security Tests - Run WITH security enabled (SEQUENTIAL, Chromium only)
    {
      name: 'security-tests',
      testDir: './tests',
      testMatch: [
        /security-enforcement\/.*\.spec\.(ts|js)/,
        /security\/.*\.spec\.(ts|js)/,
      ],
      dependencies: ['setup', 'security-shard-setup'],
      teardown: 'security-teardown',
      fullyParallel: false,
      workers: 1,
      use: {
        ...devices['Desktop Chrome'],
        headless: true,
        storageState: STORAGE_STATE,
      },
    },

    // Security Teardown - Disable ALL security modules
    // Conditionally disabled when skipSecurityDeps is true
    // When disabled, tests cannot find this project and it won't run
    {
      name: 'security-teardown',
      testMatch: skipSecurityDeps ? [] : /security-teardown\.setup\.ts/,
    },

    // Browser projects - standard Playwright pattern
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: STORAGE_STATE,
      },
      dependencies: browserDependencies,
      testMatch: /.*\.spec\.(ts|js)$/,
      testIgnore: [
        '**/frontend/**',
        '**/node_modules/**',
        '**/backend/**',
        '**/security-enforcement/**',
        '**/security/**',
      ],
    },

    {
      name: 'firefox',
      use: {
        ...devices['Desktop Firefox'],
        storageState: STORAGE_STATE,
      },
      dependencies: browserDependencies,
      testMatch: /.*\.spec\.(ts|js)$/,
      testIgnore: [
        '**/frontend/**',
        '**/node_modules/**',
        '**/backend/**',
        '**/security-enforcement/**',
        '**/security/**',
      ],
    },

    {
      name: 'webkit',
      use: {
        ...devices['Desktop Safari'],
        storageState: STORAGE_STATE,
      },
      dependencies: browserDependencies,
      testMatch: /.*\.spec\.(ts|js)$/,
      testIgnore: [
        '**/frontend/**',
        '**/node_modules/**',
        '**/backend/**',
        '**/security-enforcement/**',
        '**/security/**',
      ],
    },

    /* Test against mobile viewports. */
    // {
    //   name: 'Mobile Chrome',
    //   use: { ...devices['Pixel 5'] },
    // },
    // {
    //   name: 'Mobile Safari',
    //   use: { ...devices['iPhone 12'] },
    // },

    /* Test against branded browsers. */
    // {
    //   name: 'Microsoft Edge',
    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },
    // },
    // {
    //   name: 'Google Chrome',
    //   use: { ...devices['Desktop Chrome'], channel: 'chrome' },
    // },
  ],

  /* Run your local dev server before starting the tests */
  // webServer: {
  //   command: 'cd frontend && npm run dev',
  //   url: 'http://localhost:5173',
  //   reuseExistingServer: !process.env.CI,
  //   timeout: 120000,
  //   stdout: 'pipe',  // PHASE 1: Enable log visibility
  //   stderr: 'pipe',  // PHASE 1: Enable log visibility
  // },
});
