import { readBody, sendJson } from "./server-http-utils.ts";

const serviceId = "hwlab-project-management";

export async function handleProjectManagementProxyHttp(request, response, url, options = {}) {
  const env = options.env ?? process.env;
  const baseUrl = String(env.HWLAB_PROJECT_MANAGEMENT_URL ?? "").trim();
  if (!baseUrl) {
    sendJson(response, 503, {
      ok: false,
      error: {
        code: "project_management_service_unconfigured",
        message: "HWLAB_PROJECT_MANAGEMENT_URL is required to route project management API requests"
      },
      valuesRedacted: true
    });
    return;
  }

  if (!isAllowedProjectManagementProxyMethod(request.method, url.pathname)) {
    sendJson(response, 405, { ok: false, error: { code: "method_not_allowed", message: "Project management API method/path is not part of the public Project Management proxy contract" } });
    return;
  }

  const auth = await options.accessController.authenticate(request, { required: true });
  if (!auth.ok) {
    sendJson(response, auth.status, auth);
    return;
  }

  let upstreamUrl;
  try {
    upstreamUrl = new URL(`${url.pathname}${url.search}`, normalizedBaseUrl(baseUrl));
  } catch {
    sendJson(response, 503, {
      ok: false,
      error: { code: "project_management_service_url_invalid", message: "HWLAB_PROJECT_MANAGEMENT_URL is invalid" },
      valuesRedacted: true
    });
    return;
  }

  const headers = bridgeHeaders(request, auth);
  const timeoutMs = positiveInteger(env.HWLAB_PROJECT_MANAGEMENT_PROXY_TIMEOUT_MS, 30000);
  try {
    const body = request.method === "GET" || request.method === "HEAD" ? undefined : await readBody(request, options.bodyLimitBytes);
    const upstream = await fetch(upstreamUrl, {
      method: request.method,
      headers,
      body,
      signal: AbortSignal.timeout(timeoutMs)
    });
    const text = await upstream.text();
    response.writeHead(upstream.status, {
      "content-type": upstream.headers.get("content-type") || "application/json; charset=utf-8",
      "cache-control": upstream.headers.get("cache-control") || "no-store",
      "x-hwlab-proxy-service": serviceId
    });
    response.end(text || "\n");
  } catch (error) {
    sendJson(response, 502, {
      ok: false,
      error: {
        code: "project_management_proxy_failed",
        message: error?.message ?? "Project management proxy request failed"
      },
      valuesRedacted: true
    });
  }
}

export function isAllowedProjectManagementProxyMethod(method, pathname) {
  if (method === "GET" || method === "HEAD") return true;
  if (method === "POST" && pathname === "/v1/project-management/workbench-links") return true;
  if (method === "POST" && pathname === "/v1/project-management/mdtodo/sources") return true;
  if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+$/u.test(pathname)) return true;
  if (method === "POST" && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+\/(?:probe|reindex)$/u.test(pathname)) return true;
  if (method === "PATCH" && /^\/v1\/project-management\/mdtodo\/files\/[^/]+\/content$/u.test(pathname)) return true;
  if (method === "POST" && pathname === "/v1/project-management/mdtodo/tasks") return true;
  if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/tasks\/[^/]+$/u.test(pathname)) return true;
  return false;
}

function bridgeHeaders(request, auth) {
  const headers = new Headers();
  for (const name of ["accept", "x-request-id", "traceparent", "tracestate"]) {
    const value = request.headers[name];
    if (Array.isArray(value)) headers.set(name, value[0]);
    else if (value) headers.set(name, value);
  }
  headers.set("x-hwlab-bridge-service", "hwlab-cloud-api");
  if (auth.actor?.id) headers.set("x-hwlab-actor-id", auth.actor.id);
  if (auth.actor?.role) headers.set("x-hwlab-actor-role", auth.actor.role);
  const authMethod = auth.authMethod ?? (auth.apiKey ? "api-key" : auth.session ? "web-session" : "unknown");
  headers.set("x-hwlab-auth-method", authMethod);
  return headers;
}

function normalizedBaseUrl(value) {
  return value.endsWith("/") ? value : `${value}/`;
}

function positiveInteger(value, fallback) {
  const number = Number(value);
  return Number.isInteger(number) && number > 0 ? number : fallback;
}
