'use strict';

// This tests the creation of a single executable application that can be
// debugged using the inspector protocol with NODE_OPTIONS=--inspect-brk=0

const common = require('../common');
const assert = require('assert');
const { spawn } = require('child_process');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');

const {
  buildSEA,
  skipIfBuildSEAIsNotSupported,
} = require('../common/sea');

skipIfBuildSEAIsNotSupported();
common.skipIfInspectorDisabled();

tmpdir.refresh();

const outputFile = buildSEA(fixtures.path('sea', 'inspect'));

// Spawn the SEA with inspect option
const seaProcess = spawn(outputFile, [], {
  env: {
    ...process.env,
    NODE_OPTIONS: '--inspect-brk=0',
  },
});

let debuggerUrl = null;
let seaStderr = '';

seaProcess.stderr.setEncoding('utf8');
seaProcess.stdout.setEncoding('utf8');

seaProcess.stdout.on('data', (data) => {
  console.log(`[SEA][STDOUT] ${data}`);
});

seaProcess.stderr.on('data', common.mustCallAtLeast((data) => {
  console.log(`[SEA][STDERR] ${data}`);
  seaStderr += data;

  // Parse the debugger listening message
  const match = seaStderr.match(/Debugger listening on ws:\/\/([\d.]+):(\d+)\//);
  if (match && !debuggerUrl) {
    const host = match[1];
    const port = match[2];
    debuggerUrl = `${host}:${port}`;

    console.log(`Running ${process.execPath} inspect ${debuggerUrl}`);
    // Once we have the debugger URL, spawn the inspector CLI
    const inspectorProcess = spawn(process.execPath, ['inspect', debuggerUrl], {
      stdio: ['pipe', 'pipe', 'pipe'],
    });

    let inspectorStdout = '';
    inspectorProcess.stdout.setEncoding('utf8');
    inspectorProcess.stderr.setEncoding('utf8');

    inspectorProcess.stdout.on('data', (data) => {
      console.log(`[INSPECT][STDOUT] ${data}`);
      inspectorStdout += data;

      // Check if we successfully connected
      const matches = [...inspectorStdout.matchAll(/debug> /g)];
      if (inspectorStdout.includes(`connecting to ${host}:${port} ... ok`) &&
          matches.length >= 2) {
        // We are at the second prompt, which means we can send commands to terminate both now.
        console.log('Sending .exit command to inspector...');
        inspectorProcess.stdin.write('.exit\n');
      }
    });

    inspectorProcess.stderr.on('data', (data) => {
      console.log(`[INSPECT][STDERR] ${data}`);
    });

    inspectorProcess.on('close', common.mustCall((code) => {
      assert.strictEqual(code, 0, `Inspector process exited with code ${code}.`);
    }));

    inspectorProcess.on('error', (err) => {
      throw err;
    });
  }
}));

seaProcess.on('close', common.mustCall((code) => {
  assert.strictEqual(code, 0, `SEA process exited with code ${code}.`);
}));

seaProcess.on('error', (err) => {
  throw err;
});
