// @ts-check

/**
 * Assets watch (with webpack)
 *
 * @param {Function} callback
 */
function assetsWatch(callback) {
  const config = require("../webpack.config.js");

  const { getServerPort, getServerHost } = require("../webpack/helpers");
  const webpack = require("webpack");
  const webpackDevServer = require("webpack-dev-server");
  const configDevServer = require("../webpack/devServer");
  // @ts-ignore
  webpackDevServer.addDevServerEntrypoints(config, configDevServer);
  // @ts-ignore
  const compiler = webpack(config);
  // @ts-ignore
  const server = new webpackDevServer(compiler, configDevServer);

  server.listen(getServerPort(), getServerHost());
}

/**
 * Assets build compilation (with webpack)
 *
 * @param {Function} callback
 */
function assetsCompile(callback) {
  const fancylog = require("fancy-log");
  const webpackConfig = require("../webpack.config.js");
  // @ts-ignore
  const compiler = require("webpack")(webpackConfig);

  compiler.run((err, stats) => {
    if (err) {
      fancylog.error(err);
      // FIXME: exit build on failure
      // process.exit(1);
      callback();
      // compiler.close();
    } else {
      fancylog.info(stats.toString({ ...webpackConfig.stats, colors: true }));
      callback();
      // compiler.close();
    }
  });
}

/**
 * On production we should not remove old scripts and styles as they can be long
 * term cached with long expiry-headers. Locally we can instead clear the
 * compiled assets scripts and styles.
 */
function assetsClean(callback) {
  const del = require("del");
  const paths = require("../paths");
  const { isProduction } = require("../config");

  let toDelete = [
    // clear everything except favicons
    paths.join(paths.frontend.dest.assets, "*.*"),
    paths.join(paths.frontend.dest.images, "**/*"),
    paths.join(paths.frontend.dest.media, "**/*"),
    paths.join(paths.frontend.dest.fonts, "**/*"),
    paths.join(paths.frontend.dest.chunks, "**/*"),
    paths.join(paths.frontend.dest.entries, "**/*"),
    // clear all source maps if any
    // paths.join(paths.frontend.dest.assets, "**/*.{map,txt}"),
    // partials do not need to be removed, they can just be overwritten
  ];

  if (!isProduction) {
    toDelete = toDelete.concat([
      // paths.join(paths.frontend.dest.entries, "**/*"),
      // // clear also hot update stuff and manifest.json
      // paths.join(paths.frontend.dest.assets, "*.{js,json}"),
    ]);
  }

  return del(toDelete);
}

/**
 * Assets partials for the <head>
 */
function assetsPartialsHead() {
  const gulp = require("gulp");
  const header = require("gulp-header");
  const template = require("gulp-template");
  const paths = require("../paths");
  const { getPublicUrls, getHeaderAutogeneration } = require("../utils");
  const config = require("../config");

  return gulp
    .src(paths.join(paths.self.templates, config.filenames.assetsHeadPartial))
    .pipe(
      // @ts-ignore
      template({
        isProduction: config.isProduction,
        hasServiceWorker: config.isHttps,
        basePublicUrl: getPublicUrls().base,
        assetsFolder: paths.frontend.dest.folders.assets + "/",
        entriesPublicRelativeUrl: paths.join(
          paths.frontend.dest.folders.assets,
          paths.frontend.dest.folders.entries,
          "/"
        ),
        serviceWorkerUrl: paths.frontend.dest.relativeUrls.serviceWorker,
      })
    )
    .pipe(header(`{{-- ${getHeaderAutogeneration()} --}}\n\n`))
    .pipe(gulp.dest(paths.frontend.dest.automated));
}

/**
 * Assets partials for the <body>
 */
function assetsPartialsBody() {
  const gulp = require("gulp");
  const header = require("gulp-header");
  const template = require("gulp-template");
  const paths = require("../paths");
  const { getPublicUrls, getHeaderAutogeneration } = require("../utils");
  const config = require("../config");

  return gulp
    .src(paths.join(paths.self.templates, config.filenames.assetsBodyPartial))
    .pipe(
      // @ts-ignore
      template({
        isProduction: config.isProduction,
        basePublicUrl: getPublicUrls().base,
        assetsFolder: paths.frontend.dest.folders.assets + "/",
        entriesPublicRelativeUrl: paths.join(
          paths.frontend.dest.folders.assets,
          paths.frontend.dest.folders.entries,
          "/"
        ),
      })
    )
    .pipe(header(`{{-- ${getHeaderAutogeneration()} --}}\n\n`))
    .pipe(gulp.dest(paths.frontend.dest.automated));
}

async function assetsRoutesBarba() {
  const gulp = require("gulp");
  // const header = require("gulp-header");
  const template = require("gulp-template");
  const paths = require("../paths");
  const { getCmsRoutes } = require("../utils/route");
  const config = require("../config");

  const { routes } = await getCmsRoutes();

  return (
    gulp
      .src(paths.join(paths.self.templates, config.filenames.routesBarba))
      .pipe(
        // @ts-ignore
        template({ routes })
      )
      // .pipe(header(`{{-- ${getHeaderAutogeneration()} --}}\n\n`))
      .pipe(gulp.dest(paths.frontend.src.vendor))
  );
}

/**
 * Collect subtasks in a single task that respect the gulp async lifecycle by
 * calling the callback on completion.
 */
function assets(callback) {
  const gulp = require("gulp");
  const { isProduction } = require("../config");

  return isProduction
    ? gulp.series(
        assetsClean,
        assetsPartialsHead,
        assetsPartialsBody,
        assetsRoutesBarba,
        assetsCompile
      )(callback)
    : gulp.series(
        assetsClean,
        assetsPartialsHead,
        assetsPartialsBody,
        assetsRoutesBarba,
        assetsWatch
      )(callback);
}

module.exports = {
  assetsClean,
  assets,
};
