const isDev = require('electron-is-dev');
const path = require('path');
const { spawn } = require('child_process');
const fs = require('fs-extra');
const envPaths = require('env-paths').default('plebchan', { suffix: false });
const ps = require('node:process');
const proxyServer = require('./proxyServer');

// use this custom function instead of spawnSync for better logging
// also spawnSync might have been causing crash on start on windows
const spawnAsync = (...args) =>
  new Promise((resolve, reject) => {
    const spawedProcess = spawn(...args);
    spawedProcess.on('exit', (exitCode, signal) => {
      if (exitCode === 0) resolve();
      else
        reject(
          Error(
            `spawnAsync process '${spawedProcess.pid}' exited with code '${exitCode}' signal '${signal}'`
          )
        );
    });
    spawedProcess.stderr.on('data', (data) => console.error(data.toString()));
    spawedProcess.stdin.on('data', (data) => console.log(data.toString()));
    spawedProcess.stdout.on('data', (data) => console.log(data.toString()));
    spawedProcess.on('error', (data) => console.error(data.toString()));
  });

const startIpfs = async () => {
  const ipfsFileName = process.platform == 'win32' ? 'ipfs.exe' : 'ipfs';
  let ipfsPath = path.join(process.resourcesPath, 'bin', ipfsFileName);
  let ipfsDataPath = path.join(envPaths.data, 'ipfs');

  // test launching the ipfs binary in dev mode
  // they must be downloaded first using `yarn electron:build`
  if (isDev) {
    let binFolderName = 'win';
    if (process.platform === 'linux') {
      binFolderName = 'linux';
    }
    if (process.platform === 'darwin') {
      binFolderName = 'mac';
    }
    ipfsPath = path.join(__dirname, '..', 'bin', binFolderName, ipfsFileName);
    ipfsDataPath = path.join(__dirname, '..', '.plebbit', 'ipfs');
  }

  if (!fs.existsSync(ipfsPath)) {
    throw Error(`ipfs binary '${ipfsPath}' doesn't exist`);
  }

  console.log({ ipfsPath, ipfsDataPath });

  fs.ensureDirSync(ipfsDataPath);
  const env = { IPFS_PATH: ipfsDataPath };
  // init ipfs client on first launch
  try {
    await spawnAsync(ipfsPath, ['init'], { env, hideWindows: true });
  } catch (e) {}

  // dont use 8080 port because it's too common
  await spawnAsync(ipfsPath, ['config', 'Addresses.Gateway', '/ip4/127.0.0.1/tcp/11028'], {
    env,
    hideWindows: true,
  });

  // use different port with proxy for debugging during env
  let apiAddress = '/ip4/127.0.0.1/tcp/5001';
  if (isDev) {
    apiAddress = apiAddress.replace('5001', '5002');
    proxyServer.start({ proxyPort: 5001, targetPort: 5002 });
  }
  await spawnAsync(ipfsPath, ['config', 'Addresses.API', apiAddress], { env, hideWindows: true });

  await new Promise((resolve, reject) => {
    const ipfsProcess = spawn(
      ipfsPath,
      ['daemon', '--migrate', '--enable-pubsub-experiment', '--enable-namesys-pubsub'],
      { env, hideWindows: true }
    );
    console.log(`ipfs daemon process started with pid ${ipfsProcess.pid}`);
    let lastError;
    ipfsProcess.stderr.on('data', (data) => {
      lastError = data.toString();
      console.error(data.toString());
    });
    ipfsProcess.stdin.on('data', (data) => console.log(data.toString()));
    ipfsProcess.stdout.on('data', (data) => console.log(data.toString()));
    ipfsProcess.on('error', (data) => console.error(data.toString()));
    ipfsProcess.on('exit', () => {
      console.error(`ipfs process with pid ${ipfsProcess.pid} exited`);
      reject(Error(lastError));
    });
    process.on('exit', () => {
      try {
        ps.kill(ipfsProcess.pid);
      } catch (e) {
        console.log(e);
      }
      try {
        // sometimes ipfs doesnt exit unless we kill pid +1
        ps.kill(ipfsProcess.pid + 1);
      } catch (e) {
        console.log(e);
      }
    });
  });
};

module.exports = startIpfs;
