import { createReadStream, existsSync, readFileSync, statSync } from "node:fs";
import { createServer } from "node:http";
import { extname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const projectRoot = resolve(fileURLToPath(new URL(".", import.meta.url)));
const websiteRoot = join(projectRoot, "website");
const distRoot = join(websiteRoot, "dist");
const root = existsSync(distRoot) ? distRoot : websiteRoot;
const publicRoot = existsSync(distRoot) ? distRoot : join(websiteRoot, "public");

function loadEnvFile(file = ".env") {
  if (!existsSync(file)) return;

  const lines = readFileSync(file, "utf8").split(/\r?\n/);
  lines.forEach((line) => {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) return;

    const separatorIndex = trimmed.indexOf("=");
    if (separatorIndex < 1) return;

    const key = trimmed.slice(0, separatorIndex).trim();
    const value = trimmed.slice(separatorIndex + 1).trim().replace(/^["']|["']$/g, "");
    if (!process.env[key]) process.env[key] = value;
  });
}

loadEnvFile();

const port = Number(process.env.PORT || 5173);

const types = {
  ".html": "text/html; charset=utf-8",
  ".css": "text/css; charset=utf-8",
  ".js": "text/javascript; charset=utf-8",
  ".json": "application/json; charset=utf-8",
  ".svg": "image/svg+xml",
  ".ico": "image/x-icon",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".webp": "image/webp"
};

function isInsideRoot(parent, file) {
  const relativePath = relative(parent, file);
  return Boolean(relativePath) && !relativePath.startsWith("..") && !relativePath.startsWith("/");
}

function fileIfReadable(file) {
  return isInsideRoot(root, file) && existsSync(file) && statSync(file).isFile() ? file : null;
}

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

  const candidate = fileIfReadable(resolve(root, `.${requested}`));
  if (candidate) return candidate;

  const publicCandidate = fileIfReadable(resolve(publicRoot, `.${requested}`));
  if (publicCandidate && isInsideRoot(publicRoot, publicCandidate)) return publicCandidate;

  return join(root, "index.html");
}

const server = createServer((req, res) => {
  const file = resolvePath(req.url || "/");
  if (!file || !existsSync(file)) {
    res.writeHead(404);
    res.end("Not found");
    return;
  }

  res.writeHead(200, {
    "Content-Type": types[extname(file)] || "application/octet-stream",
    "Cache-Control": "no-store"
  });
  createReadStream(file).pipe(res);
});

server.on("error", (error) => {
  if (error.code === "EADDRINUSE") {
    console.error(`Port ${port} is already in use. Set PORT to another value.`);
    process.exit(1);
  }

  console.error(error);
  process.exit(1);
});

server.listen(port, () => {
  console.log(`Sokko G dev server running at http://localhost:${port}`);
});
