/* eslint-disable @typescript-eslint/no-var-requires */
import Koa from "koa";
import {
  contentSecurityPolicy,
  dnsPrefetchControl,
  referrerPolicy,
} from "koa-helmet";
import mount from "koa-mount";
import enforceHttps from "koa-sslify";
import env from "@server/env";
import Logger from "@server/logging/logger";
import emails from "../emails";
import routes from "../routes";
import api from "../routes/api";
import auth from "../routes/auth";

const isProduction = env.NODE_ENV === "production";
const isTest = env.NODE_ENV === "test";
// Construct scripts CSP based on services in use by this installation
const defaultSrc = ["'self'"];
const scriptSrc = [
  "'self'",
  "'unsafe-inline'",
  "'unsafe-eval'",
  "gist.github.com",
];

if (env.GOOGLE_ANALYTICS_ID) {
  scriptSrc.push("www.google-analytics.com");
}

if (env.CDN_URL) {
  scriptSrc.push(env.CDN_URL);
  defaultSrc.push(env.CDN_URL);
}

export default function init(app: Koa = new Koa()): Koa {
  if (isProduction) {
    // Force redirect to HTTPS protocol unless explicitly disabled
    if (process.env.FORCE_HTTPS !== "false") {
      app.use(
        enforceHttps({
          // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ trustProtoHeader: boolean; }' ... Remove this comment to see the full error message
          trustProtoHeader: true,
        })
      );
    } else {
      Logger.warn("Enforced https was disabled with FORCE_HTTPS env variable");
    }

    // trust header fields set by our proxy. eg X-Forwarded-For
    app.proxy = true;
  } else if (!isTest) {
    const convert = require("koa-convert");
    const webpack = require("webpack");
    const devMiddleware = require("koa-webpack-dev-middleware");
    const hotMiddleware = require("koa-webpack-hot-middleware");
    const config = require("../../webpack.config.dev");
    const compile = webpack(config);

    /* eslint-enable global-require */
    const middleware = devMiddleware(compile, {
      // display no info to console (only warnings and errors)
      noInfo: true,
      // display nothing to the console
      quiet: false,
      watchOptions: {
        poll: 1000,
        ignored: ["node_modules", "flow-typed", "server", "build", "__mocks__"],
      },
      // public path to bind the middleware to
      // use the same as in webpack
      publicPath: config.output.publicPath,
      // options for formatting the statistics
      stats: {
        colors: true,
      },
    });
    app.use(async (ctx, next) => {
      ctx.webpackConfig = config;
      ctx.devMiddleware = middleware;
      await next();
    });
    app.use(convert(middleware));
    app.use(
      convert(
        hotMiddleware(compile, {
          // @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' ty... Remove this comment to see the full error message
          log: (...args) => Logger.info("lifecycle", ...args),
          path: "/__webpack_hmr",
          heartbeat: 10 * 1000,
        })
      )
    );
    app.use(mount("/emails", emails));
  }

  app.use(mount("/auth", auth));
  app.use(mount("/api", api));
  // Sets common security headers by default, such as no-sniff, hsts, hide powered
  // by etc, these are applied after auth and api so they are only returned on
  // standard non-XHR accessed routes
  app.use(async (ctx, next) => {
    ctx.set("Permissions-Policy", "interest-cohort=()");
    await next();
  });
  app.use(
    contentSecurityPolicy({
      directives: {
        defaultSrc,
        scriptSrc,
        styleSrc: ["'self'", "'unsafe-inline'", "github.githubassets.com"],
        imgSrc: ["*", "data:", "blob:"],
        frameSrc: ["*"],
        connectSrc: ["*"], // Do not use connect-src: because self + websockets does not work in
        // Safari, ref: https://bugs.webkit.org/show_bug.cgi?id=201591
      },
    })
  );
  // Allow DNS prefetching for performance, we do not care about leaking requests
  // to our own CDN's
  app.use(
    dnsPrefetchControl({
      allow: true,
    })
  );
  app.use(
    referrerPolicy({
      policy: "no-referrer",
    })
  );
  app.use(mount(routes));
  return app;
}
