import { file } from "bun";
import { join, normalize, extname } from "node:path";
import { statSync, existsSync, readdirSync } from "node:fs";

const ROOT = import.meta.dir;
const PORT = Number(process.env.PORT ?? 3000);

const MIME: Record<string, string> = {
  ".html": "text/html; charset=utf-8",
  ".css": "text/css; charset=utf-8",
  ".js": "application/javascript; charset=utf-8",
  ".mjs": "application/javascript; charset=utf-8",
  ".jsx": "application/javascript; charset=utf-8",
  ".json": "application/json; charset=utf-8",
  ".svg": "image/svg+xml",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".gif": "image/gif",
  ".webp": "image/webp",
  ".ico": "image/x-icon",
  ".woff": "font/woff",
  ".woff2": "font/woff2",
  ".ttf": "font/ttf",
  ".otf": "font/otf",
  ".map": "application/json",
};

function safeJoin(root: string, urlPath: string): string | null {
  const decoded = decodeURIComponent(urlPath);
  const resolved = normalize(join(root, decoded));
  if (!resolved.startsWith(root)) return null;
  return resolved;
}

function listing(dir: string, urlPath: string): Response {
  const entries = readdirSync(dir, { withFileTypes: true })
    .sort((a, b) => (a.isDirectory() === b.isDirectory() ? a.name.localeCompare(b.name) : a.isDirectory() ? -1 : 1));
  const base = urlPath.endsWith("/") ? urlPath : urlPath + "/";
  const rows = entries
    .map((e) => {
      const name = e.name + (e.isDirectory() ? "/" : "");
      const href = encodeURI(base + name);
      return `<li><a href="${href}">${name}</a></li>`;
    })
    .join("");
  const up = urlPath !== "/" ? `<li><a href="${encodeURI(base + "..")}">../</a></li>` : "";
  const html = `<!doctype html><meta charset="utf-8"><title>Index of ${urlPath}</title>
<style>body{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;padding:24px;max-width:800px;margin:auto}h1{font-size:14px;color:#555}ul{list-style:none;padding:0}li{padding:4px 0}a{text-decoration:none;color:#0366d6}a:hover{text-decoration:underline}</style>
<h1>Index of ${urlPath}</h1><ul>${up}${rows}</ul>`;
  return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });
}

const server = Bun.serve({
  port: PORT,
  development: true,
  async fetch(req) {
    const url = new URL(req.url);
    const path = safeJoin(ROOT, url.pathname);
    if (!path) return new Response("Forbidden", { status: 403 });

    if (!existsSync(path)) return new Response("Not Found", { status: 404 });

    const stat = statSync(path);
    if (stat.isDirectory()) {
      const indexPath = join(path, "index.html");
      if (existsSync(indexPath)) {
        return new Response(file(indexPath), {
          headers: { "content-type": MIME[".html"] },
        });
      }
      return listing(path, url.pathname);
    }

    const ext = extname(path).toLowerCase();
    const type = MIME[ext] ?? "application/octet-stream";
    return new Response(file(path), {
      headers: {
        "content-type": type,
        "cache-control": "no-cache",
      },
    });
  },
  error(err: Error) {
    console.error(err);
    return new Response("Internal Server Error", { status: 500 });
  },
});

console.log(`sketch-v3 → http://localhost:${server.port}`);
