import { window, workspace, OutputChannel, Terminal } from 'vscode';
import { fork, ChildProcess } from 'child_process';
import * as rl from 'readline';

let outputChannels: { [windowId: string]: OutputChannel | undefined } = {};
const runningProcesses: Map<number, Process> = new Map();

interface Process {
  process: ChildProcess;
  cmd: string;
  windowId: string;
}

export function getNpmBin() {
  return workspace.getConfiguration('npm')['bin'] || 'npm';
}

export function runCommandInIntegratedTerminal(
  terminalId: string,
  cmd: string,
  args: string[],
  cwd: string | undefined,
): Thenable<number> {
  const cmd_args = Array.from(args);
  const terminal = getTerminal(terminalId);
  terminal.show();
  if (cwd) {
    // Replace single backslash with double backslash.
    const textCwd = cwd.replace(/\\/g, '\\\\');
    terminal.sendText(['cd', `"${textCwd}"`].join(' '));
  }
  cmd_args.splice(0, 0, cmd);
  terminal.sendText(cmd_args.join(' '));
  return terminal.processId;
}

export function runScriptInOutputWindow(
  windowId: string,
  scriptPath: string,
  args: string[],
  cwd: string | undefined,
  env?: NodeJS.ProcessEnv,
  onMessage?: (message: any, outputChannel: OutputChannel) => void,
  onExit?: (code: number, signal: string, outputChannel: OutputChannel) => void,
) {
  const outputChannel = getOutputChannel(windowId);

  const p = fork(scriptPath, args, { cwd, env, silent: true });

  runningProcesses.set(p.pid, { process: p, cmd: scriptPath, windowId });

  rl.createInterface({ input: p.stdout, terminal: false }).on('line', (line: string) => {
    outputChannel!.appendLine(line);
  });
  rl.createInterface({ input: p.stderr, terminal: false }).on('line', (line: string) => {
    outputChannel!.appendLine(line);
  });
  p.on('message', message => {
    if (onMessage) {
      onMessage(message, outputChannel);
    }
  });
  p.on('exit', (code: number, signal: string) => {
    if (onExit) {
      onExit(code, signal, outputChannel!);
    }
    runningProcesses.delete(p.pid);
  });

  outputChannel.show();
  return p.pid;
}

export function stopScriptInOutputWindow(pid: number) {
  const process = runningProcesses.get(pid);
  if (process) {
    process.process.kill('SIGTERM');
  }
}

export function getOutputChannel(windowId: string) {
  let outputChannel = outputChannels[windowId];
  if (!outputChannel) {
    outputChannel = window.createOutputChannel(windowId);
    outputChannels[windowId] = outputChannel;
  }
  return outputChannel;
}

export function getTerminal(terminalId: string) {
  let terminal = window.terminals.find(term => term.name === terminalId);
  if (!terminal) {
    terminal = window.createTerminal(terminalId);
  }
  return terminal;
}
