import {
  forwardToAnthropic
} from "./chunk-VFSHH6I2.js";
import {
  findByRawKey,
  hydratePermissions
} from "./chunk-UW7HVMAP.js";
import {
  getConfig
} from "./chunk-2MAQ6JFL.js";
import {
  isAnthropicApiKey,
  isPassthroughAllowedPath
} from "./chunk-ZX44HT4Y.js";
import {
  badRequest,
  forbidden,
  unauthorized
} from "./chunk-EZYKRG4W.js";

// src/auth/middleware.ts
var TENANT_HEADER = "x-agentstep-tenant";
var TENANT_HEADER_RE = /^[a-zA-Z0-9_-]{1,64}$/;
function readTenantHeader(request) {
  const raw = request.headers.get(TENANT_HEADER);
  if (raw === null) return null;
  if (!TENANT_HEADER_RE.test(raw)) {
    console.warn(`[auth] rejected malformed ${TENANT_HEADER} header`);
    throw badRequest(`malformed ${TENANT_HEADER} header`);
  }
  return raw;
}
function extractKey(request) {
  const xKey = request.headers.get("x-api-key");
  if (xKey && xKey.length > 0) return xKey;
  const auth = request.headers.get("authorization") || request.headers.get("Authorization");
  if (auth) {
    const m = /^Bearer\s+(.+)$/i.exec(auth);
    if (m) return m[1];
  }
  try {
    const url = new URL(request.url);
    const qKey = url.searchParams.get("x-api-key");
    if (qKey && qKey.length > 0 && !qKey.startsWith("sk-ant-api")) return qKey;
  } catch {
  }
  return null;
}
async function authenticate(request) {
  const key = extractKey(request);
  if (!key) throw unauthorized();
  const tenantHeader = readTenantHeader(request);
  const row = findByRawKey(key);
  if (isAnthropicApiKey(key)) {
    if (!getConfig().anthropicPassthroughEnabled) throw unauthorized();
    if (tenantHeader !== null) {
      throw badRequest(`${TENANT_HEADER} header is not allowed in passthrough mode`);
    }
    return {
      keyId: "passthrough",
      name: "anthropic-passthrough",
      permissions: { admin: false, scope: null },
      tenantId: null,
      isGlobalAdmin: false,
      actingAsTenant: null,
      budgetUsd: null,
      rateLimitRpm: null,
      spentUsd: 0,
      mode: "passthrough",
      passthroughKey: key
    };
  }
  if (!row) throw unauthorized();
  const permissions = hydratePermissions(row.permissions_json);
  const isGlobalAdmin = row.tenant_id === null && permissions.admin;
  let actingAsTenant = null;
  if (tenantHeader !== null) {
    if (isGlobalAdmin) {
      actingAsTenant = tenantHeader;
    } else if (row.tenant_id !== null && row.tenant_id === tenantHeader) {
      actingAsTenant = tenantHeader;
    } else {
      throw forbidden(`${TENANT_HEADER} header does not match the key's tenant`);
    }
  }
  return {
    keyId: row.id,
    name: row.name,
    permissions,
    tenantId: row.tenant_id,
    // Global admin: null tenant + admin bit. Legacy ["*"] keys hydrate
    // as {admin: true, scope: null} with tenantId still null, so they
    // remain global admins across a 0.4 → 0.5 upgrade. This is the
    // documented default; `gateway tenants migrate-legacy` is the
    // explicit step that changes it.
    isGlobalAdmin,
    actingAsTenant,
    budgetUsd: row.budget_usd ?? null,
    rateLimitRpm: row.rate_limit_rpm ?? null,
    spentUsd: row.spent_usd ?? 0,
    mode: "gateway"
  };
}
async function authenticateAndIntercept(request) {
  const auth = await authenticate(request);
  if (auth.mode !== "passthrough") return { kind: "auth", auth };
  const url = new URL(request.url);
  if (!isPassthroughAllowedPath(url.pathname)) {
    throw unauthorized();
  }
  const response = await forwardToAnthropic(request, url.pathname, {
    apiKey: auth.passthroughKey
  });
  return { kind: "response", response };
}

export {
  extractKey,
  authenticate,
  authenticateAndIntercept
};
