import type { Request, Response, NextFunction } from "express";
import express from "express";
import bodyParser from "body-parser";
import config from "config";
import basePath from "../basepath";
import cookieParser from "cookie-parser";
import http from "http";
import expressWs from "express-ws";

process.env.TZ = "Etc/GMT";

console.log("Loading ketr.ketran");

const app = express();
const server = http.createServer(app);

app.use(cookieParser());

expressWs(app, server);

require("../console-line"); /* Monkey-patch console.log with line numbers */

// Temporary debug routes (dev-only). Mount before static so we can
// inspect what the server receives for base-prefixed requests.
try {
  // import the debug router using ESM style; fallback to require at runtime if needed
  // (some dev environments may still emit JS commonjs files)
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const debugRouter = require("../routes/debug").default || require("../routes/debug");
  app.use(basePath, debugRouter);
} catch (e: any) {
  console.error("Failed to mount debug routes (src):", (e && e.stack) || e);
}

const frontendPath = (config.get("frontendPath") as string).replace(/\/$/, "") + "/",
  serverConfig = config.get("server") as { port: number };

console.log("Hosting server from: " + basePath);

// DB handles are initialized by the modules below; we don't need file-scoped vars here.

app.use((req: Request, _res: Response, next: NextFunction) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

app.use(bodyParser.json());

/* App is behind an nginx proxy which we trust, so use the remote address
 * set in the headers */
app.set("trust proxy", true);

app.set("basePath", basePath);
// basepath is a simple exported string
// eslint-disable-next-line @typescript-eslint/no-var-requires
const basepathRouter = require("../routes/basepath").default || require("../routes/basepath");
app.use(basePath, basepathRouter);

/* Handle static files first so excessive logging doesn't occur */
app.use(basePath, express.static(frontendPath, { index: false }));

// index route (may be ESM default export)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const index = require("../routes/index").default || require("../routes/index");

if (config.has("admin")) {
  const admin = config.get("admin");
  app.set("admin", admin);
}

// games router
// eslint-disable-next-line @typescript-eslint/no-var-requires
const gamesRouter = require("../routes/games").default || require("../routes/games");
app.use(`${basePath}api/v1/games`, gamesRouter);

/* Allow loading of the app w/out being logged in */
app.use(basePath, index);

/* /games loads the default index */
app.use(basePath + "games", index);

/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
app.use(basePath, index);

/**
 * Create HTTP server and listen for new connections
 */
app.set("port", serverConfig.port);

process.on("SIGINT", () => {
  console.log("Gracefully shutting down from SIGINT (Ctrl-C) in 2 seconds");
  setTimeout(() => process.exit(-1), 2000);
  server.close(() => process.exit(1));
});

if (process.env['NODE_ENV'] !== 'test') {
  console.log("Opening server.");
  server.listen(serverConfig.port, () => {
    console.log(`http/ws server listening on ${serverConfig.port}`);
  });
}

server.on("error", function (error: any) {
  if (error.syscall !== "listen") {
    throw error;
  }

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case "EACCES":
      console.error(serverConfig.port + " requires elevated privileges");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(serverConfig.port + " is already in use");
      process.exit(1);
      break;
    default:
      throw error;
  }
});

export { app, server };
