/**
 * The proxy: decide which upstream path is allowed, build a clean header set,
 * and forward to Anthropic.
 *
 * Two deliberate narrowings live here. First, an entitled caller may reach only
 * the upstream paths you opt into (default "/v1/messages"), not any Anthropic
 * endpoint your key can touch. Second, the upstream request carries only the
 * three headers it needs, not whatever the client sent (cookies, arbitrary
 * anthropic-beta flags, the app token).
 */
import {
  ANTHROPIC_BASE,
  DEFAULT_ANTHROPIC_VERSION,
  type Env,
} from "./env.ts";

/** Resolve the upstream path, or null when the requested path is not allowed. */
export function resolveUpstreamPath(
  pathname: string,
  allowed: Set<string>,
): string | null {
  return allowed.has(pathname) ? pathname : null;
}

/**
 * Build the only headers the upstream call needs. Nothing the client sent is
 * forwarded by default: we copy content-type, set the Anthropic version, and
 * inject the key.
 */
export function buildUpstreamHeaders(request: Request, env: Env): Headers {
  const headers = new Headers();
  const contentType = request.headers.get("content-type");
  if (contentType) headers.set("content-type", contentType);
  headers.set(
    "anthropic-version",
    env.ANTHROPIC_VERSION ?? DEFAULT_ANTHROPIC_VERSION,
  );
  headers.set("x-api-key", env.ANTHROPIC_API_KEY);
  return headers;
}

/**
 * Forward the (already authorized) request to Anthropic. When the spend gate
 * has read the body, the same text is re-sent verbatim via `body`; otherwise
 * the request body streams through untouched. The query string is dropped on
 * purpose: resolveUpstreamPath matched only the pathname, so nothing unvetted
 * rides along.
 */
export function forwardToAnthropic(
  request: Request,
  env: Env,
  upstreamPath: string,
  body?: string,
): Promise<Response> {
  const headers = buildUpstreamHeaders(request, env);
  // The spend gate parsed the body as JSON, so pin the declared type to what
  // was actually inspected.
  if (body !== undefined) headers.set("content-type", "application/json");
  return fetch(ANTHROPIC_BASE + upstreamPath, {
    method: request.method,
    headers,
    body: body ?? request.body,
    // @ts-expect-error duplex is required when streaming a request body
    duplex: "half",
  });
}
