#!/usr/bin/env node

const {createServer, build, createLogger} = require('vite');
const electronPath = require('electron');
const {spawn} = require('child_process');
const node = 'node' + (process.platform === 'win32' ? '.exe' : '');

const server = !process.argv.includes('--noserver');

/** @type 'production' | 'development'' */
const mode = process.env.MODE = process.env.MODE || 'development';


/** @type {import('vite').LogLevel} */
const logLevel = 'info';


/** Messages on stderr that match any of the contained patterns will be stripped from output */
const stderrFilterPatterns = [
  /**
   * warning about devtools extension
   * @see https://github.com/cawa-93/vite-electron-builder/issues/492
   * @see https://github.com/MarshallOfSound/electron-devtools-installer/issues/143
   */
  /ExtensionLoadWarning/,
];

const setupServerPackageWatcher = () => {
  const logger = createLogger(logLevel, {
    prefix: '[srvr]',
    allowClearScreen: false,
  });

  let spawnProcess = null;

  const processDied = () => {
    logger.error('Server has died.', {timestamp: true});
    spawnProcess = null;
  };

  return build({
    mode,
    logLevel,
    build: {
      watch: {},
    },
    configFile: 'packages/server/vite.config.js',
    plugins: [{
      name: 'reload-server-on-server-package-change',
      writeBundle() {
        if (spawnProcess !== null) {
          spawnProcess.off('exit', processDied);
          spawnProcess.kill('SIGINT');
          spawnProcess = null;
        }

        spawnProcess = spawn(node, ['./index.cjs'], {
          cwd: './packages/server/dist',
        });

        /** Proxy all logs */
        spawnProcess.stdout.on('data', d => d.toString().trim() && d.toString().trim().split('\n').forEach(str => logger.info(str, {timestamp: true})));

        /** Proxy error logs but stripe some noisy messages. See {@link stderrFilterPatterns} */
        spawnProcess.stderr.on('data', d => {
          const data = d.toString().trim();
          if (!data) return;
          const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
          if (mayIgnore) return;
          data.split('\n').forEach(d => logger.error(d, {timestamp: true}));
        });

        /** Stops the watch script when the application has been quit */
        spawnProcess.on('exit', processDied);
      },
    }],
  });
};

/**
 * Setup watcher for `main` package
 * On file changed it totally re-launch electron app.
 * @param {import('vite').ViteDevServer} watchServer Renderer watch server instance.
 * Needs to set up `VITE_DEV_SERVER_URL` environment variable from {@link import('vite').ViteDevServer.resolvedUrls}
 */

const setupMainPackageWatcher = ({resolvedUrls}) => {
  process.env.VITE_DEV_SERVER_URL = resolvedUrls.local[0];

  const logger = createLogger(logLevel, {
    prefix: '[main]',
    allowClearScreen: false,
  });

  /** @type {ChildProcessWithoutNullStreams | null} */
  let spawnProcess = null;

  return build({
    mode,
    logLevel,
    build: {
      /**
       * Set to {} to enable rollup watcher
       * @see https://vitejs.dev/config/build-options.html#build-watch
       */
      watch: {},
    },
    configFile: 'packages/main/vite.config.js',
    plugins: [{
      name: 'reload-app-on-main-package-change',
      writeBundle() {
        /** Kill electron ff process already exist */
        if (spawnProcess !== null) {
          spawnProcess.off('exit', process.exit);
          spawnProcess.kill('SIGINT');
          spawnProcess = null;
        }

        /** Spawn new electron process */
        spawnProcess = spawn(String(electronPath), ['.']);

        /** Proxy all logs */
        spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), {timestamp: true}));

        /** Proxy error logs but stripe some noisy messages. See {@link stderrFilterPatterns} */
        spawnProcess.stderr.on('data', d => {
          const data = d.toString().trim();
          if (!data) return;
          const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
          if (mayIgnore) return;
          logger.error(data, {timestamp: true});
        });

        /** Stops the watch script when the application has been quit */
        spawnProcess.on('exit', process.exit);
      },
    }],
  });

};


/**
 * Setup watcher for `preload` package
 * On file changed it reload web page.
 * @param {import('vite').ViteDevServer} watchServer Renderer watch server instance.
 * Required to access the web socket of the page. By sending the `full-reload` command to the socket, it reloads the web page.
 */
const setupPreloadPackageWatcher = ({ws}) =>
  build({
    mode,
    logLevel,
    build: {
      /**
       * Set to {} to enable rollup watcher
       * @see https://vitejs.dev/config/build-options.html#build-watch
       */
      watch: {},
    },
    configFile: 'packages/preload/vite.config.js',
    plugins: [{
      name: 'reload-page-on-preload-package-change',
      writeBundle() {
        ws.send({
          type: 'full-reload',
        });
      },
    }],
  });


(async () => {
  try {

    /**
     * Renderer watcher
     * This must be the first,
     * because the {@link setupMainPackageWatcher} and {@link setupPreloadPackageWatcher} depend on the renderer params
     */
    const rendererWatchServer = await createServer({
      mode,
      logLevel,
      configFile: 'packages/renderer/vite.config.js',
    });

    /**
     * Should launch watch server before create other watchers
     */
    await rendererWatchServer.listen();

    /**
     * See {@link setupPreloadPackageWatcher} JSDoc
     */
    await setupPreloadPackageWatcher(rendererWatchServer);

    /**
     * See {@link setupMainPackageWatcher} JSDoc
     */
    await setupMainPackageWatcher(rendererWatchServer);
    if(server) await setupServerPackageWatcher();
  } catch (e) {
    console.error(e);
    process.exit(1);
  }
})();
