const { existsSync } = require('fs');
const { spawn } = require('child_process');
const { performance } = require('perf_hooks');

function parseArgs(argv) {
  const options = {
    entry: 'dist/main.js',
    port: 4100,
    timeout: 30000,
    path: '/api/v1/health',
  };

  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index];
    const next = argv[index + 1];

    if (arg === '--entry' && next) {
      options.entry = next;
      index += 1;
      continue;
    }

    if (arg === '--port' && next) {
      options.port = Number(next);
      index += 1;
      continue;
    }

    if (arg === '--timeout' && next) {
      options.timeout = Number(next);
      index += 1;
      continue;
    }

    if (arg === '--path' && next) {
      options.path = next.startsWith('/') ? next : `/${next}`;
      index += 1;
    }
  }

  if (!existsSync(options.entry)) {
    throw new Error(`Entry file not found: ${options.entry}. Run "npm run build" first.`);
  }

  if (!Number.isInteger(options.port) || options.port <= 0) {
    throw new Error('port must be a positive integer');
  }

  if (!Number.isFinite(options.timeout) || options.timeout <= 0) {
    throw new Error('timeout must be a positive number of milliseconds');
  }

  return options;
}

async function waitForHealth(url, timeoutMs) {
  const deadline = Date.now() + timeoutMs;
  let lastError = null;

  while (Date.now() < deadline) {
    try {
      const response = await fetch(url, { method: 'GET' });
      if (response.ok) {
        const body = await response.text();
        return { status: response.status, body };
      }
    } catch (error) {
      lastError = error;
    }

    await new Promise((resolve) => setTimeout(resolve, 250));
  }

  if (lastError instanceof Error) {
    throw new Error(`Startup timeout. Last error: ${lastError.message}`);
  }

  throw new Error('Startup timeout. Health endpoint did not become ready.');
}

async function terminate(child) {
  if (child.exitCode !== null) {
    return;
  }

  child.kill();
  await new Promise((resolve) => {
    child.once('exit', resolve);
    setTimeout(resolve, 5000);
  });
}

async function main() {
  const options = parseArgs(process.argv.slice(2));
  const url = `http://127.0.0.1:${options.port}${options.path}`;
  const stdoutLines = [];
  const stderrLines = [];

  const startedAt = performance.now();
  const child = spawn(process.execPath, [options.entry], {
    cwd: process.cwd(),
    env: {
      ...process.env,
      PORT: String(options.port),
      PUBLIC_BASE_URL: `http://127.0.0.1:${options.port}`,
    },
    stdio: ['ignore', 'pipe', 'pipe'],
  });

  child.stdout.on('data', (chunk) => {
    stdoutLines.push(...String(chunk).split(/\r?\n/).filter(Boolean));
    if (stdoutLines.length > 20) {
      stdoutLines.splice(0, stdoutLines.length - 20);
    }
  });

  child.stderr.on('data', (chunk) => {
    stderrLines.push(...String(chunk).split(/\r?\n/).filter(Boolean));
    if (stderrLines.length > 20) {
      stderrLines.splice(0, stderrLines.length - 20);
    }
  });

  try {
    const healthResult = await waitForHealth(url, options.timeout);
    const readyMs = performance.now() - startedAt;

    console.log(
      JSON.stringify(
        {
          entry: options.entry,
          url,
          startupMs: Number(readyMs.toFixed(2)),
          healthStatus: healthResult.status,
          recentStdout: stdoutLines,
          recentStderr: stderrLines,
        },
        null,
        2,
      ),
    );
  } catch (error) {
    const reason = error instanceof Error ? error.message : String(error);
    throw new Error(
      [
        reason,
        `Child exit code: ${child.exitCode ?? 'still-running'}`,
        `Recent stdout: ${stdoutLines.join(' | ') || 'none'}`,
        `Recent stderr: ${stderrLines.join(' | ') || 'none'}`,
      ].join('\n'),
    );
  } finally {
    await terminate(child);
  }
}

main().catch((error) => {
  console.error(error instanceof Error ? error.message : String(error));
  process.exitCode = 1;
});
