const { app, BrowserWindow, dialog, ipcMain, shell } = require('electron');
const { spawn, spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const projectRoot = path.resolve(__dirname, '..', '..');
const outputDir = path.join(projectRoot, 'dist-release-final');
const toolLogPath = path.join(__dirname, 'oem-tool.log');
const statePath = path.join(__dirname, 'active-run.json');
const toolAppName = 'OEM Batch Builder Tool';

app.setName(toolAppName);
app.setAppUserModelId('com.oem.batch-builder-tool');

let mainWindow = null;
let running = false;
let currentProcess = null;
let latestResults = [];
let activePollTimer = null;
let activeLogOffset = 0;
let activeSessionDir = '';
let activeResultFile = '';
let activeWorkerLogFile = '';
let activeStageStatusFile = '';
let allowQuitWhileRunning = false;

const gotSingleInstanceLock = app.requestSingleInstanceLock();
if (!gotSingleInstanceLock) {
  appendToolLog(`single-instance lock denied pid=${process.pid} argv=${safeJson(process.argv)}`);
  app.quit();
}

appendToolLog(`process start pid=${process.pid} ppid=${process.ppid} cwd=${process.cwd()} argv=${safeJson(process.argv)}`);

function appendToolLog(message) {
  try {
    fs.appendFileSync(toolLogPath, `[${new Date().toISOString()}] ${message}\n`, 'utf8');
  } catch {
    // ignore logging failure
  }
}

function safeJson(value) {
  try {
    return JSON.stringify(value);
  } catch {
    return String(value);
  }
}

function sendLog(payload) {
  const message = String(payload.message || '');
  if (payload.level === 'stdout' && shouldSuppressWindowLog(message)) return;
  const displayPayload = {
    ...payload,
    message: message.length > 6000 ? `${message.slice(0, 6000)}\n... [full output is in build-output.log]` : message,
  };
  appendToolLog(`${payload.level || 'info'} ${payload.jobId ? `[${payload.jobId}] ` : ''}${displayPayload.message || ''}`);
  if (!mainWindow || mainWindow.isDestroyed()) return;
  mainWindow.webContents.send('oem-tool:log', {
    time: new Date().toISOString(),
    ...displayPayload,
  });
}

function shouldSuppressWindowLog(message) {
  const text = String(message || '');
  return text.includes('[OK] Copied qrcode')
    || text.includes('[OK] Copied logo')
    || text.includes('[OK] Copied login-bg')
    || text.includes('[SKIP] pattern not matched')
    || text.includes('Processing: F:');
}

function readState() {
  try {
    if (!fs.existsSync(statePath)) return null;
    return JSON.parse(fs.readFileSync(statePath, 'utf8').replace(/^\uFEFF/, ''));
  } catch (error) {
    appendToolLog(`readState failed: ${error && error.message ? error.message : String(error)}`);
    return null;
  }
}

function writeState(payload) {
  try {
    fs.writeFileSync(statePath, JSON.stringify(payload, null, 2), 'utf8');
  } catch (error) {
    appendToolLog(`writeState failed: ${error && error.message ? error.message : String(error)}`);
  }
}

function clearStateIfFinished(payload) {
  writeState({
    running: false,
    finishedAt: new Date().toISOString(),
    outputDir,
    results: payload.results || [],
    message: payload.message || '',
    success: Boolean(payload.success),
    sessionDir: activeSessionDir,
  });
}

function createWindow() {
  appendToolLog('createWindow start');
  const staleFailure = repairStaleRunningState();
  if (staleFailure) {
    appendToolLog(`repaired stale active run before window creation: ${staleFailure.message}`);
  }
  mainWindow = new BrowserWindow({
    width: 1320,
    height: 860,
    minWidth: 1120,
    minHeight: 720,
    title: toolAppName,
    backgroundColor: '#f6f7fb',
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.cjs'),
      contextIsolation: true,
      nodeIntegration: false,
    },
  });
  mainWindow.webContents.on('console-message', (_event, level, message, line, sourceId) => {
    appendToolLog(`renderer console level=${level} ${sourceId || ''}:${line || 0} ${message}`);
  });
  mainWindow.webContents.on('preload-error', (_event, preloadPath, error) => {
    appendToolLog(`preload-error ${preloadPath}: ${error && error.stack ? error.stack : error}`);
  });
  mainWindow.webContents.on('did-crash', (_event, killed) => {
    appendToolLog(`renderer did-crash killed=${killed}`);
  });
  mainWindow.webContents.on('destroyed', () => {
    appendToolLog('webContents destroyed');
  });
  mainWindow.on('unresponsive', () => {
    appendToolLog('window unresponsive');
  });
  mainWindow.on('responsive', () => {
    appendToolLog('window responsive');
  });

  const showMainWindow = () => {
    if (!mainWindow || mainWindow.isDestroyed()) return;
    appendToolLog(`showMainWindow visible=${mainWindow.isVisible()} minimized=${mainWindow.isMinimized()}`);
    mainWindow.setTitle(toolAppName);
    if (mainWindow.isMinimized()) mainWindow.restore();
    mainWindow.center();
    mainWindow.show();
    mainWindow.focus();
    mainWindow.moveTop();
    appendToolLog(`showMainWindow done visible=${mainWindow.isVisible()} bounds=${JSON.stringify(mainWindow.getBounds())}`);
  };

  mainWindow.loadFile(path.join(__dirname, 'index.html'));
  showMainWindow();
  mainWindow.on('page-title-updated', event => {
    event.preventDefault();
    if (mainWindow && !mainWindow.isDestroyed()) mainWindow.setTitle(toolAppName);
  });
  mainWindow.once('ready-to-show', showMainWindow);
  mainWindow.webContents.once('did-finish-load', () => setTimeout(showMainWindow, 50));
  setTimeout(showMainWindow, 2000);
  mainWindow.webContents.on('render-process-gone', (_event, details) => {
    appendToolLog(`render-process-gone: ${JSON.stringify(details)}`);
    if (mainWindow && !mainWindow.isDestroyed()) {
      mainWindow.reload();
    }
  });
  mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => {
    appendToolLog(`did-fail-load: ${errorCode} ${errorDescription} ${validatedURL}`);
  });
  mainWindow.on('close', event => {
    if (running && !allowQuitWhileRunning) {
      const payload = pollActiveSession();
      if (payload) {
        finishActiveSession({ ...payload, outputDir });
        return;
      }
      const pid = getKnownWorkerPid();
      if (!pid || !isProcessAlive(pid)) {
        markStaleRunStopped(`window close allowed because worker is not alive. pid=${pid || 'unknown'}`);
        return;
      }
      event.preventDefault();
      appendToolLog(`window close requested while worker is running; keep tool window open. pid=${pid}`);
      if (mainWindow && !mainWindow.isDestroyed()) {
        mainWindow.show();
        mainWindow.focus();
      }
    }
  });
  mainWindow.on('closed', () => {
    appendToolLog('window closed');
    mainWindow = null;
    if (running) {
      setTimeout(() => {
        if (!mainWindow) createWindow();
      }, 300);
    }
  });
}

function readNewWorkerLogs(workerLogFile) {
  if (!fs.existsSync(workerLogFile)) return;
  const stat = fs.statSync(workerLogFile);
  if (stat.size <= activeLogOffset) return;

  const fd = fs.openSync(workerLogFile, 'r');
  const buffer = Buffer.alloc(stat.size - activeLogOffset);
  fs.readSync(fd, buffer, 0, buffer.length, activeLogOffset);
  fs.closeSync(fd);
  activeLogOffset = stat.size;

  const lines = buffer.toString('utf8').split(/\r?\n/).filter(Boolean);
  for (const line of lines) {
    try {
      sendLog(JSON.parse(line));
    } catch {
      sendLog({ level: 'info', message: line });
    }
  }
}

function stopActivePoll() {
  if (activePollTimer) {
    clearInterval(activePollTimer);
    activePollTimer = null;
  }
}

function setActiveSession(sessionDir) {
  activeSessionDir = sessionDir;
  activeResultFile = path.join(sessionDir, 'result.json');
  activeWorkerLogFile = path.join(sessionDir, 'worker.log');
  activeStageStatusFile = path.join(sessionDir, 'stage-status.json');
  activeLogOffset = 0;
}

function readJsonFile(filePath) {
  try {
    if (!filePath || !fs.existsSync(filePath)) return null;
    return JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, ''));
  } catch (error) {
    appendToolLog(`readJsonFile failed ${filePath}: ${error && error.message ? error.message : String(error)}`);
    return null;
  }
}

function readWorkerPidFromSession(sessionDir) {
  try {
    const pidFile = path.join(sessionDir, 'worker.pid');
    if (!fs.existsSync(pidFile)) return null;
    const parsed = Number(fs.readFileSync(pidFile, 'utf8').replace(/^\uFEFF/, '').trim());
    return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
  } catch (error) {
    appendToolLog(`readWorkerPidFromSession failed: ${error && error.message ? error.message : String(error)}`);
    return null;
  }
}

function waitForWorkerPidFromSession(sessionDir, timeoutMs = 5000) {
  const started = Date.now();
  const sleepBuffer = new SharedArrayBuffer(4);
  const sleepView = new Int32Array(sleepBuffer);
  while (Date.now() - started < timeoutMs) {
    const pid = readWorkerPidFromSession(sessionDir);
    if (pid) return pid;
    Atomics.wait(sleepView, 0, 0, 100);
  }
  return null;
}

function makeJobsFailure(jobs, sessionDir, message) {
  const safeJobs = Array.isArray(jobs) ? jobs : [];
  return {
    success: false,
    results: safeJobs.map(job => ({
      id: job.id,
      brandName: job.brandName,
      version: job.version,
      success: false,
      outputDir,
      buildOutputFile: sessionDir ? path.join(sessionDir, 'build-output.log') : '',
      artifacts: [],
      message,
    })),
    outputDir,
    message,
  };
}

function getKnownWorkerPid() {
  const state = readState();
  if (currentProcess && currentProcess.pid) return currentProcess.pid;
  if (state && state.pid) return state.pid;
  if (activeSessionDir) return readWorkerPidFromSession(activeSessionDir);
  if (state && state.sessionDir) return readWorkerPidFromSession(state.sessionDir);
  return null;
}

function repairStaleRunningState() {
  const state = readState();
  if (!state || !state.running || !state.sessionDir) return null;

  const resultPath = state.resultFile || path.join(state.sessionDir, 'result.json');
  if (fs.existsSync(resultPath)) return null;

  const pid = state.pid || readWorkerPidFromSession(state.sessionDir);
  if (pid && isProcessAlive(pid)) return null;
  const launcherStartedAt = state.launcherStartedAt ? Date.parse(state.launcherStartedAt) : 0;
  if (!pid && launcherStartedAt && Date.now() - launcherStartedAt < 15000) return null;

  if (activeSessionDir !== state.sessionDir) {
    setActiveSession(state.sessionDir);
  }

  const failed = makeMissingResultFailure({
    ...state,
    pid,
  });
  finishActiveSession(failed);
  return failed;
}

function markStaleRunStopped(message) {
  stopActivePoll();
  running = false;
  currentProcess = null;
  writeState({
    ...(readState() || {}),
    running: false,
    stoppedAt: new Date().toISOString(),
    outputDir,
    sessionDir: activeSessionDir,
    message,
  });
  appendToolLog(message);
}

function pollActiveSession() {
  if (!activeSessionDir) return null;
  readNewWorkerLogs(activeWorkerLogFile);
  if (!fs.existsSync(activeResultFile)) return null;
  try {
    return JSON.parse(fs.readFileSync(activeResultFile, 'utf8'));
  } catch (error) {
    return {
      success: false,
      results: latestResults,
      outputDir,
      message: error && error.message ? error.message : String(error),
    };
  }
}

function makeMissingResultFailure(state) {
  const stage = readJsonFile(path.join(state.sessionDir, 'stage-status.json')) || readJsonFile(activeStageStatusFile);
  const jobs = Array.isArray(state.jobs) ? state.jobs : [];
  const firstJob = jobs[0] || {};
  const stageText = stage && stage.stage ? ` Last stage: ${stage.stage} (${stage.status || 'unknown'}).` : '';
  const message = `Worker process ${state.pid || 'unknown'} stopped without producing result.json.${stageText} See ${state.workerLogFile || activeWorkerLogFile}`;
  return {
    success: false,
    results: jobs.length > 0 ? jobs.map(job => ({
      id: job.id,
      brandName: job.brandName,
      version: job.version,
      success: false,
      outputDir,
      buildOutputFile: path.join(state.sessionDir, 'build-output.log'),
      artifacts: [],
      message,
      stage,
    })) : [{
      id: firstJob.id || 'oem-0',
      brandName: firstJob.brandName || 'OEM',
      success: false,
      outputDir,
      buildOutputFile: path.join(state.sessionDir, 'build-output.log'),
      artifacts: [],
      message,
      stage,
    }],
    outputDir,
    message,
  };
}

function finishActiveSession(payload) {
  stopActivePoll();
  latestResults = payload.results || [];
  running = false;
  currentProcess = null;
  clearStateIfFinished(payload);
  sendLog({ level: payload.success ? 'info' : 'error', message: payload.message || 'Build queue finished' });
}

function finishStartupFailure(jobs, sessionDir, message) {
  finishActiveSession(makeJobsFailure(jobs, sessionDir, message));
}

function findNodeExe() {
  const candidates = [
    process.env.npm_node_execpath,
    process.env.NODE_EXE,
  ].filter(Boolean);

  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) return candidate;
  }

  const result = spawnSync('where.exe', ['node.exe'], {
    cwd: projectRoot,
    encoding: 'utf8',
    windowsHide: true,
  });
  const found = String(result.stdout || '')
    .split(/\r?\n/)
    .map(line => line.trim())
    .find(Boolean);
  if (found && fs.existsSync(found)) return found;
  return 'node.exe';
}

function launchDetachedWorker(workerPath, relativeJobsFile, relativeSessionDir) {
  const sessionDir = path.resolve(projectRoot, relativeSessionDir);
  fs.mkdirSync(sessionDir, { recursive: true });

  const nodeExe = findNodeExe();
  const stdoutFd = fs.openSync(path.join(sessionDir, 'worker-main.stdout.log'), 'a');
  const stderrFd = fs.openSync(path.join(sessionDir, 'worker-main.stderr.log'), 'a');
  appendToolLog(`launchDetachedWorker node=${nodeExe} worker=${workerPath} jobs=${relativeJobsFile} session=${relativeSessionDir}`);
  const worker = spawn(nodeExe, [workerPath, relativeJobsFile, relativeSessionDir], {
    cwd: projectRoot,
    windowsHide: true,
    detached: true,
    stdio: ['ignore', stdoutFd, stderrFd],
  });
  fs.closeSync(stdoutFd);
  fs.closeSync(stderrFd);

  if (!worker.pid) {
    throw new Error('Build worker did not return a PID.');
  }
  fs.writeFileSync(path.join(sessionDir, 'worker.pid'), String(worker.pid), 'utf8');
  worker.unref();

  return {
    launcherPid: null,
    workerPid: worker.pid,
  };
}

function startActivePoll() {
  stopActivePoll();
  activePollTimer = setInterval(() => {
    const payload = pollActiveSession();
    if (payload) {
      finishActiveSession({ ...payload, outputDir });
    }
  }, 700);
}

function isProcessAlive(pid) {
  if (!pid) return false;
  try {
    process.kill(Number(pid), 0);
    return true;
  } catch {
    return false;
  }
}

ipcMain.handle('oem-tool:diagnostic', async (_event, payload) => {
  const message = payload && payload.message ? payload.message : 'renderer diagnostic';
  appendToolLog(`renderer diagnostic: ${message} ${safeJson(payload && payload.payload ? payload.payload : {})}`);
  return { ok: true };
});

ipcMain.handle('oem-tool:pick-image', async () => {
  const result = await dialog.showOpenDialog(mainWindow, {
    properties: ['openFile'],
    filters: [{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'webp', 'ico', 'icns'] }],
  });
  if (result.canceled) return null;
  return result.filePaths[0] || null;
});

ipcMain.handle('oem-tool:run-batch', async (_event, jobs) => {
  appendToolLog(`run-batch invoked jobs=${Array.isArray(jobs) ? jobs.length : 'invalid'}`);
  if (running) {
    appendToolLog('run-batch rejected: already running');
    return { success: false, message: 'Build queue is already running', results: latestResults, outputDir };
  }
  if (!Array.isArray(jobs) || jobs.length === 0) {
    appendToolLog('run-batch rejected: no jobs');
    return { success: false, message: 'No OEM jobs selected', results: [], outputDir };
  }

  const sessionDir = path.join(__dirname, 'runs', `${Date.now()}`);
  const jobsFile = path.join(sessionDir, 'jobs.json');

  try {
    fs.mkdirSync(sessionDir, { recursive: true });
    fs.writeFileSync(jobsFile, JSON.stringify(jobs, null, 2), 'utf8');

    running = true;
    latestResults = [];
    setActiveSession(sessionDir);

    const relativeSessionDir = path.relative(projectRoot, sessionDir);
    const relativeJobsFile = path.relative(projectRoot, jobsFile);
    const workerPath = path.join('tools', 'oem-batch-builder', 'worker.cjs');

    writeState({
      running: true,
      startedAt: new Date().toISOString(),
      pid: null,
      launcherPid: null,
      outputDir,
      sessionDir,
      jobsFile,
      resultFile: activeResultFile,
      workerLogFile: activeWorkerLogFile,
      stageStatusFile: activeStageStatusFile,
      jobs,
      message: 'Starting worker',
      launcherStartedAt: new Date().toISOString(),
    });
    appendToolLog(`run-batch state written session=${sessionDir}`);

    const launchInfo = launchDetachedWorker(workerPath, relativeJobsFile, relativeSessionDir);
    const workerPid = launchInfo.workerPid || readWorkerPidFromSession(sessionDir);
    appendToolLog(`run-batch launchDetachedWorker returned launcherPid=${launchInfo.launcherPid} workerPid=${workerPid || ''}`);
    currentProcess = workerPid ? { pid: workerPid } : null;

    writeState({
      ...(readState() || {}),
      running: true,
      pid: workerPid || null,
      launcherPid: launchInfo.launcherPid,
      outputDir,
      sessionDir,
      jobsFile,
      resultFile: activeResultFile,
      workerLogFile: activeWorkerLogFile,
      stageStatusFile: activeStageStatusFile,
      jobs,
      launcherStartedAt: new Date().toISOString(),
      message: workerPid ? `Worker started. pid=${workerPid}` : `Worker launcher started. launcherPid=${launchInfo.launcherPid}`,
    });
    sendLog({ level: 'info', message: workerPid ? `Started background worker. pid=${workerPid} jobs=${jobs.length}` : `Started worker launcher. pid=${launchInfo.launcherPid} jobs=${jobs.length}` });
    appendToolLog(`background worker launcherPid=${launchInfo.launcherPid} workerPid=${workerPid || ''} worker=${workerPath} jobs=${relativeJobsFile}`);
    startActivePoll();
    appendToolLog(`run-batch accepted session=${sessionDir} launcherPid=${launchInfo.launcherPid} workerPid=${workerPid || ''}`);
    return {
      success: true,
      accepted: true,
      running: true,
      sessionDir,
      outputDir,
      message: `Build started. Logs: ${activeWorkerLogFile}`,
      results: [],
    };
  } catch (error) {
    const message = `Failed to start build worker: ${error && error.message ? error.message : String(error)}`;
    appendToolLog(`${message}\n${error && error.stack ? error.stack : ''}`);
    if (running || activeSessionDir === sessionDir) {
      finishStartupFailure(jobs, sessionDir, message);
    } else {
      appendToolLog(message);
    }
    return { success: false, running: false, outputDir, message, results: latestResults };
  }
});

ipcMain.handle('oem-tool:get-status', async () => {
  const staleFailure = repairStaleRunningState();
  if (staleFailure) {
    return {
      running: false,
      results: staleFailure.results || [],
      outputDir,
      sessionDir: activeSessionDir,
      message: staleFailure.message,
    };
  }

  const state = readState();
  if (state && state.running && state.sessionDir) {
    if (!fs.existsSync(state.sessionDir)) {
      const failed = {
        success: false,
        results: [],
        outputDir,
        message: `Previous build state points to a missing session directory: ${state.sessionDir}`,
      };
      finishActiveSession(failed);
      return { running: false, results: [], outputDir, sessionDir: state.sessionDir, message: failed.message };
    }
    if (!state.pid) {
      const workerPid = readWorkerPidFromSession(state.sessionDir);
      if (workerPid) {
        state.pid = workerPid;
        writeState({ ...state, pid: workerPid });
      } else {
        const launcherStartedAt = state.launcherStartedAt ? Date.parse(state.launcherStartedAt) : 0;
        if (launcherStartedAt && Date.now() - launcherStartedAt < 15000) {
          return { running: true, results: latestResults, outputDir, sessionDir: state.sessionDir, workerLogFile: state.workerLogFile, message: 'Worker launcher is starting' };
        }
        const failed = makeMissingResultFailure(state);
        finishActiveSession(failed);
        return { running: false, results: failed.results, outputDir, sessionDir: state.sessionDir, message: failed.message };
      }
    }
    if (activeSessionDir !== state.sessionDir) {
      setActiveSession(state.sessionDir);
      running = true;
      startActivePoll();
    }
    const payload = pollActiveSession();
    if (payload) {
      finishActiveSession({ ...payload, outputDir });
      return { running: false, results: latestResults, outputDir, sessionDir: state.sessionDir };
    }
    if (state.pid && !isProcessAlive(state.pid)) {
      const failed = makeMissingResultFailure(state);
      finishActiveSession(failed);
      return { running: false, results: failed.results, outputDir, sessionDir: state.sessionDir, message: failed.message };
    }
    return { running: true, results: latestResults, outputDir, sessionDir: state.sessionDir, workerLogFile: state.workerLogFile, stage: readJsonFile(state.stageStatusFile) };
  }
  return { running, results: latestResults, outputDir, sessionDir: activeSessionDir };
});

ipcMain.handle('oem-tool:cancel', async () => {
  const state = readState();
  const pid = currentProcess && currentProcess.pid ? currentProcess.pid : state && state.pid;
  if (!pid) return { success: false, message: 'No running build process' };
  spawn('taskkill', ['/F', '/T', '/PID', String(pid)], { windowsHide: true, stdio: 'ignore' });
  stopActivePoll();
  currentProcess = null;
  running = false;
  writeState({ running: false, cancelledAt: new Date().toISOString(), outputDir, sessionDir: activeSessionDir });
  sendLog({ level: 'warn', message: 'Build worker cancelled' });
  return { success: true };
});

ipcMain.handle('oem-tool:open-output-dir', async () => {
  fs.mkdirSync(outputDir, { recursive: true });
  await shell.openPath(outputDir);
  return { success: true, outputDir };
});

app.on('second-instance', () => {
  appendToolLog('second-instance received');
  if (!mainWindow || mainWindow.isDestroyed()) {
    createWindow();
    return;
  }
  if (mainWindow.isMinimized()) mainWindow.restore();
  mainWindow.center();
  mainWindow.show();
  mainWindow.focus();
});

if (gotSingleInstanceLock) {
  app.whenReady().then(() => {
    appendToolLog(`app ready pid=${process.pid}`);
    createWindow();
  }).catch(error => {
    appendToolLog(`app ready failed: ${error && error.stack ? error.stack : error}`);
  });
}
app.on('will-quit', () => {
  appendToolLog(`app will-quit running=${running} activeSession=${activeSessionDir || ''}`);
});
app.on('quit', (_event, exitCode) => {
  appendToolLog(`app quit exitCode=${exitCode} running=${running} activeSession=${activeSessionDir || ''}`);
});
app.on('before-quit', event => {
  appendToolLog(`before-quit running=${running} allowQuitWhileRunning=${allowQuitWhileRunning}`);
  if (running) {
    if (!allowQuitWhileRunning) {
      event.preventDefault();
      appendToolLog('before-quit ignored while worker is running');
      if (!mainWindow || mainWindow.isDestroyed()) {
        createWindow();
      } else {
        if (mainWindow.isMinimized()) mainWindow.restore();
        mainWindow.show();
        mainWindow.focus();
      }
      return;
    }
    writeState({
      ...(readState() || {}),
      running: true,
      outputDir,
      sessionDir: activeSessionDir,
      resultFile: activeResultFile,
      workerLogFile: activeWorkerLogFile,
      stageStatusFile: activeStageStatusFile,
    });
  }
});
app.on('window-all-closed', () => {
  if (running && !allowQuitWhileRunning) {
    appendToolLog('window-all-closed ignored while worker is running');
    return;
  }
  app.quit();
});

process.on('uncaughtException', error => {
  appendToolLog(`uncaughtException: ${error && error.stack ? error.stack : error}`);
  sendLog({ level: 'error', message: `Tool error: ${error && error.message ? error.message : String(error)}` });
});

process.on('unhandledRejection', error => {
  appendToolLog(`unhandledRejection: ${error && error.stack ? error.stack : error}`);
  sendLog({ level: 'error', message: `Tool error: ${error && error.message ? error.message : String(error)}` });
});

process.on('SIGINT', () => {
  appendToolLog(`process SIGINT running=${running} activeSession=${activeSessionDir || ''}`);
});

process.on('SIGTERM', () => {
  appendToolLog(`process SIGTERM running=${running} activeSession=${activeSessionDir || ''}`);
});

process.on('beforeExit', code => {
  appendToolLog(`process beforeExit code=${code} running=${running} activeSession=${activeSessionDir || ''}`);
});

process.on('exit', code => {
  appendToolLog(`process exit code=${code} running=${running} activeSession=${activeSessionDir || ''}`);
});
