import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { resolveBearer } from "./auth.mjs";
import { exactCandidate } from "./candidate.mjs";
import { badRequest, LabError } from "./errors.mjs";
import { createLabRuntime } from "./runtime.mjs";

const overviewUrl = new URL("../index.html", import.meta.url);
const readmeUrl = new URL("../README.md", import.meta.url);
const siteRootUrl = new URL("../../../", import.meta.url);

const staticRoutes = new Map([
  ["/styles.css", { url: new URL("styles.css", siteRootUrl), type: "text/css; charset=utf-8" }],
  ["/assets/favicon-16x16.png", { url: new URL("assets/favicon-16x16.png", siteRootUrl), type: "image/png" }],
  ["/assets/favicon-32x32.png", { url: new URL("assets/favicon-32x32.png", siteRootUrl), type: "image/png" }],
  ["/assets/field-grid.png", { url: new URL("assets/field-grid.png", siteRootUrl), type: "image/png" }],
  ["/assets/lumyn-mark.png", { url: new URL("assets/lumyn-mark.png", siteRootUrl), type: "image/png" }],
  ["/CONTRIBUTING.md", { url: new URL("CONTRIBUTING.md", siteRootUrl), type: "text/markdown; charset=utf-8" }],
  ["/LICENSE", { url: new URL("LICENSE", siteRootUrl), type: "text/plain; charset=utf-8" }],
  ["/README.md", { url: readmeUrl, type: "text/markdown; charset=utf-8" }],
  ["/src/service.mjs", { url: new URL("../src/service.mjs", import.meta.url), type: "text/javascript; charset=utf-8" }],
  ["/src/sor.mjs", { url: new URL("../src/sor.mjs", import.meta.url), type: "text/javascript; charset=utf-8" }],
  ["/test/service.test.mjs", { url: new URL("../test/service.test.mjs", import.meta.url), type: "text/javascript; charset=utf-8" }],
  ["/package.json", { url: new URL("../package.json", import.meta.url), type: "application/json; charset=utf-8" }],
]);

function sendJson(response, status, body) {
  response.writeHead(status, {
    "content-type": "application/json; charset=utf-8",
    "cache-control": "no-store",
    "x-content-type-options": "nosniff",
  });
  response.end(`${JSON.stringify(body, null, 2)}\n`);
}

async function readJson(request) {
  let bytes = 0;
  const chunks = [];
  for await (const chunk of request) {
    bytes += chunk.length;
    if (bytes > 64 * 1024) {
      throw badRequest("request_too_large", "Request bodies are limited to 64 KiB.");
    }
    chunks.push(chunk);
  }
  try {
    return JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
  } catch {
    throw badRequest("invalid_json", "The request body must be valid JSON.");
  }
}

function principalFor(request) {
  return resolveBearer(request.headers.authorization);
}

export function createLabHttpServer(runtime = createLabRuntime()) {
  const server = createServer(async (request, response) => {
    try {
      const url = new URL(request.url ?? "/", "http://127.0.0.1");
      const path = url.pathname;

      if (request.method === "GET" && path === "/") {
        response.writeHead(200, {
          "content-type": "text/html; charset=utf-8",
          "cache-control": "no-store",
          "x-content-type-options": "nosniff",
        });
        response.end(await readFile(overviewUrl));
        return;
      }
      if (request.method === "GET" && staticRoutes.has(path)) {
        const asset = staticRoutes.get(path);
        response.writeHead(200, {
          "content-type": asset.type,
          "cache-control": "no-store",
          "x-content-type-options": "nosniff",
        });
        response.end(await readFile(asset.url));
        return;
      }
      if (request.method === "GET" && path === "/health") {
        sendJson(response, 200, { status: "ok", service: "claims-denial-teaching-lab" });
        return;
      }
      if (request.method === "GET" && path === "/v1/candidate") {
        sendJson(response, 200, exactCandidate);
        return;
      }
      if (request.method === "GET" && path.startsWith("/v1/cases/")) {
        const caseId = decodeURIComponent(path.slice("/v1/cases/".length));
        sendJson(response, 200, runtime.service.getCase({ principal: principalFor(request), caseId }));
        return;
      }
      if (request.method === "POST" && path === "/v1/recommendations") {
        const body = await readJson(request);
        const result = await runtime.service.recommend({
          principal: principalFor(request),
          caseId: body.caseId,
          expectedCaseRevision: body.expectedCaseRevision,
          candidateId: body.candidateId,
          candidateDigest: request.headers["x-candidate-digest"],
          idempotencyKey: request.headers["idempotency-key"],
          failureMode: request.headers["x-lab-failure"],
        });
        sendJson(response, 201, result);
        return;
      }
      if (request.method === "GET" && path.startsWith("/v1/operations/")) {
        const operationId = decodeURIComponent(path.slice("/v1/operations/".length));
        sendJson(
          response,
          200,
          runtime.service.getOperation({ principal: principalFor(request), operationId }),
        );
        return;
      }
      if (request.method === "GET" && path === "/v1/telemetry") {
        sendJson(
          response,
          200,
          runtime.service.getTelemetry({ principal: principalFor(request) }),
        );
        return;
      }

      sendJson(response, 404, {
        error: { code: "not_found", message: "Route not found." },
      });
    } catch (error) {
      const status = error instanceof LabError ? error.status : 500;
      sendJson(response, status, {
        error: {
          code: error instanceof LabError ? error.code : "internal_error",
          message: error instanceof LabError ? error.message : "Unexpected lab error.",
          ...(error instanceof LabError && error.details ? { details: error.details } : {}),
        },
      });
    }
  });

  return { server, runtime };
}
