"use strict";

/* eslint-disable @typescript-eslint/no-require-imports -- Node SEA CommonJS entry point */

const { spawn } = require("node:child_process");
const http = require("node:http");
const { getAsset, getAssetKeys } = require("node:sea");

const HOST = "127.0.0.1";
const FIRST_PORT = 4173;
const LAST_PORT = 4193;
const localePaths = new Set([
  "en",
  "de",
  "es-ES",
  "fr",
  "it",
  "ja",
  "ru",
  "zh-CN",
  "zh-TW",
]);

const mimeTypes = new Map([
  [".css", "text/css; charset=utf-8"],
  [".html", "text/html; charset=utf-8"],
  [".ico", "image/x-icon"],
  [".js", "text/javascript; charset=utf-8"],
  [".json", "application/json; charset=utf-8"],
  [".mjs", "text/javascript; charset=utf-8"],
  [".png", "image/png"],
  [".svg", "image/svg+xml"],
  [".txt", "text/plain; charset=utf-8"],
  [".webp", "image/webp"],
  [".xml", "application/xml; charset=utf-8"],
  [".zip", "application/zip"],
]);

const assetNames = new Set(
  getAssetKeys()
    .filter((key) => key.startsWith("site/"))
    .map((key) => key.slice(5)),
);

function extensionOf(fileName) {
  const dot = fileName.lastIndexOf(".");
  return dot < 0 ? "" : fileName.slice(dot).toLowerCase();
}

function resolveAsset(requestUrl) {
  let pathname;
  try {
    pathname = decodeURIComponent(new URL(requestUrl, "http://localhost").pathname);
  } catch {
    return null;
  }

  if (pathname.includes("\0") || pathname.includes("\\")) return null;
  const segments = pathname.split("/").filter(Boolean);
  if (segments.some((segment) => segment === "." || segment === "..")) return null;

  let relativePath = segments.join("/");
  if (!relativePath) relativePath = "index.html";
  else if (pathname.endsWith("/")) relativePath += "/index.html";
  return relativePath;
}

function setSecurityHeaders(response) {
  response.setHeader("X-Content-Type-Options", "nosniff");
  response.setHeader("Referrer-Policy", "no-referrer");
  response.setHeader("X-Frame-Options", "SAMEORIGIN");
  response.setHeader(
    "Content-Security-Policy",
    "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'",
  );
}

const server = http.createServer((request, response) => {
  setSecurityHeaders(response);
  if (request.method !== "GET" && request.method !== "HEAD") {
    response.writeHead(405, { Allow: "GET, HEAD" });
    response.end("Method not allowed");
    return;
  }

  const relativePath = resolveAsset(request.url || "/");
  if (relativePath === "index.html") {
    response.writeHead(308, { Location: "/en/" });
    response.end();
    return;
  }
  if (relativePath && localePaths.has(relativePath)) {
    response.writeHead(308, { Location: `/${relativePath}/` });
    response.end();
    return;
  }
  if (!relativePath || !assetNames.has(relativePath)) {
    response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
    response.end("Not found");
    return;
  }

  const extension = extensionOf(relativePath);
  const content = Buffer.from(getAsset(`site/${relativePath}`));
  response.setHeader("Content-Type", mimeTypes.get(extension) || "application/octet-stream");
  response.setHeader("Content-Length", content.length);
  response.setHeader(
    "Cache-Control",
    relativePath.startsWith("assets/") ? "public, max-age=31536000, immutable" : "no-cache",
  );
  response.writeHead(200);
  response.end(request.method === "HEAD" ? undefined : content);
});

function openBrowser(url) {
  const child = spawn(
    process.env.ComSpec || "cmd.exe",
    ["/d", "/s", "/c", "start", "", url],
    { detached: true, stdio: "ignore", windowsHide: true },
  );
  child.unref();
}

function listen(port) {
  const onError = (error) => {
    server.off("listening", onListening);
    if (error.code === "EADDRINUSE" && port < LAST_PORT) {
      listen(port + 1);
      return;
    }
    console.error("\nLocal server could not start:", error.message);
    console.error("Der lokale Server konnte nicht gestartet werden.");
    process.exitCode = 1;
  };
  const onListening = () => {
    server.off("error", onError);
    const url = `http://${HOST}:${port}/en/`;
    console.log("\nFactorio Production Calculator 1.0.0");
    console.log(`Local address / Lokale Adresse: ${url}`);
    console.log("Blueprint generator and Help are included. No Factorio installation is required to preview layouts.");
    console.log("Close this window or press Ctrl+C to stop. / Zum Beenden Fenster schließen oder Strg+C drücken.\n");
    openBrowser(url);
  };

  server.once("error", onError);
  server.once("listening", onListening);
  server.listen(port, HOST);
}

process.title = "Factorio Production Calculator 1.0.0";
listen(FIRST_PORT);
