/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

// Note: can be converted to ts file once node 20 support is dropped.
// Node 20 does not support --experimental-strip-types flag.

import {spawn, execSync} from 'node:child_process';
import {readFile} from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';

const args = process.argv.slice(2);
const userArgs = args.filter(arg => !arg.startsWith('-'));
const flags = args.filter(arg => arg.startsWith('-'));

const files = [];

let shouldRetry = false;
const retryIndex = flags.indexOf('--retry');
if (retryIndex !== -1) {
  shouldRetry = true;
  flags.splice(retryIndex, 1);
}

if (userArgs.length > 0) {
  for (const arg of userArgs) {
    // Map .ts files to build/ .js files
    let testPath = arg;
    if (testPath.endsWith('.ts')) {
      testPath = testPath.replace(/\.ts$/, '.js');
      if (!testPath.startsWith('build/')) {
        testPath = path.join('build', testPath);
      }
    }
    files.push(testPath);
  }
} else {
  const isNode20 = process.version.startsWith('v20.');
  if (flags.includes('--test-only')) {
    if (isNode20) {
      throw new Error(`--test-only is not supported for Node 20`);
    }
    const {glob} = await import('node:fs/promises');
    for await (const tsFile of glob('tests/**/*.test.ts')) {
      const content = await readFile(tsFile, 'utf8');
      if (content.includes('.only(')) {
        files.push(path.join('build', tsFile.replace(/\.ts$/, '.js')));
      }
    }
    if (files.length === 0) {
      console.warn('no files contain .only');
      process.exit(0);
    }
  } else if (files.length === 0) {
    if (isNode20) {
      files.push('build/tests');
    } else {
      files.push('build/tests/**/*.test.js');
    }
  }
}

const nodeArgs = [
  '--import',
  './build/tests/setup.js',
  '--no-warnings=ExperimentalWarning',
  '--test-reporter',
  (process.env['NODE_TEST_REPORTER'] ?? process.env['CI']) ? 'spec' : 'dot',
  '--test-force-exit',
  '--test',
  '--test-timeout=120000',
  ...flags,
  ...files,
];

function _installChrome(version) {
  try {
    return execSync(
      `npx puppeteer browsers install chrome@${version} --format "{{path}}"`,
    )
      .toString()
      .trim();
  } catch (e) {
    console.error(`Failed to install Chrome ${version}:`, e);
    process.exit(1);
  }
}

async function runTests(attempt) {
  if (attempt > 1) {
    console.log(`\nRun attempt ${attempt}...\n`);
  }
  return new Promise(resolve => {
    const child = spawn('node', nodeArgs, {
      stdio: 'inherit',
      env: {
        ...process.env,
        CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: true,
        CHROME_DEVTOOLS_MCP_CRASH_ON_UNCAUGHT: true,
        CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS: true,
        ...(process.env['RUNNER_DEBUG'] === '1' ? {DEBUG: 'puppeteer:*'} : {}),
      },
    });

    child.on('close', code => {
      resolve(code);
    });
  });
}

const maxAttempts = shouldRetry ? 3 : 1;
let exitCode = 1;

for (let i = 1; i <= maxAttempts; i++) {
  exitCode = await runTests(i);
  if (exitCode === 0) {
    break;
  }
}

process.exit(exitCode ?? 1);
