const fs = require("fs");
const path = require("path");
const MFS = require("memory-fs");
const webpack = require("webpack");
const chokidar = require("chokidar");
const devMiddleware = require("webpack-dev-middleware");
const hotMiddleware = require("webpack-hot-middleware");
const clientConfig = require("./webpack.client.config");
const serverConfig = require("./webpack.server.config");

function readFile(fs, file) {
  try {
    return fs.readFileSync(path.join(clientConfig.output.path, file), "utf-8");
  } catch (err) {
    console.log(err);
  }
}

const setupDevServer = (app, templatePath, cb) => {
  let template;
  let serverBundle;
  let clientManifest;
  let ready;

  const readyPromise = new Promise(r => {
    ready = r;
  });

  const update = () => {
    if (serverBundle && clientManifest) {
      ready();

      cb(serverBundle, {
        template,
        clientManifest,
      });
    }
  };

  /* ------------------------------------------------------------------
   * Watch `template.index.html` for changes
   * ------------------------------------------------------------------*/

  template = fs.readFileSync(templatePath, "utf-8");

  chokidar.watch(templatePath).on("change", () => {
    template = fs.readFileSync(templatePath, "utf-8");
    console.log("Index template file updated");

    update();
  });

  /* ------------------------------------------------------------------
   * Hot middleware setup
   * ------------------------------------------------------------------*/

  // Update webpack client config to use hot middleware

  clientConfig.entry.app = [
    "webpack-hot-middleware/client",
    clientConfig.entry.app,
  ];

  clientConfig.output.filename = "[name].js";

  clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin());

  // Use webpack-dev-middleware

  const clientCompiler = webpack(clientConfig);

  const webpackDevMiddleware = devMiddleware(clientCompiler, {
    publicPath: clientConfig.output.publicPath,
    logLevel: "silent",
  });

  app.use(webpackDevMiddleware);

  // Use webpack-hot-middleware

  const webpackHotMiddleware = hotMiddleware(clientCompiler, {
    heartbeat: 5000,
    log: false,
  });

  app.use(webpackHotMiddleware);

  /* ------------------------------------------------------------------
   * Watch for webpack compiler "done" hook and update clientManifest
   * ------------------------------------------------------------------*/

  clientCompiler.hooks.done.tap("clientCompiler", stats => {
    stats = stats.toJson();
    stats.errors.forEach(err => console.error(err));
    stats.warnings.forEach(err => console.warn(err));

    if (stats.errors.length) {
      return;
    }

    clientManifest = JSON.parse(
      readFile(webpackDevMiddleware.fileSystem, "vue-ssr-client-manifest.json"),
    );

    update();
  });

  /* ------------------------------------------------------------------
   * Watch and update server renderer
   * ------------------------------------------------------------------*/

  const serverCompiler = webpack(serverConfig);
  const mfs = new MFS();

  serverCompiler.outputFileSystem = mfs;
  serverCompiler.watch({}, (err, stats) => {
    if (err) {
      throw err;
    }

    stats = stats.toJson();

    if (stats.errors.length) {
      return;
    }

    // read serverBundle generated by vue-ssr-webpack-plugin
    serverBundle = JSON.parse(readFile(mfs, "vue-ssr-server-bundle.json"));

    update();
  });

  return readyPromise;
};

module.exports = setupDevServer;
