import { cookies } from "next/headers.js";
import { verifyJwt } from "./jwt.js";
import { getSharedApiKeyAuthCache } from "./apiKeyAuthCache.js";
import { isTransientDbError } from "@open-mercato/shared/lib/db/pg-errors";
const TENANT_COOKIE_NAME = "om_selected_tenant";
const ORGANIZATION_COOKIE_NAME = "om_selected_org";
const ALL_ORGANIZATIONS_COOKIE_VALUE = "__all__";
const SUPERADMIN_ROLE = "superadmin";
class AuthResolutionUnavailableError extends Error {
  constructor(cause) {
    super("Auth resolution unavailable");
    this.name = "AuthResolutionUnavailableError";
    if (cause !== void 0) this.cause = cause;
  }
}
const TRUSTED_AUTH_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("open-mercato.auth.trustedContext");
function attachTrustedAuthContext(request, envelope) {
  ;
  request[TRUSTED_AUTH_CONTEXT_SYMBOL] = envelope;
  return request;
}
function readTrustedAuthContext(request) {
  const carrier = request;
  const envelope = carrier[TRUSTED_AUTH_CONTEXT_SYMBOL];
  if (!envelope || typeof envelope !== "object") return null;
  const candidate = envelope;
  if (!("auth" in candidate)) return null;
  return candidate;
}
function decodeCookieValue(raw) {
  if (raw === void 0) return null;
  try {
    const decoded = decodeURIComponent(raw);
    return decoded ?? null;
  } catch {
    return raw ?? null;
  }
}
function readCookieFromHeader(header, name) {
  if (!header) return void 0;
  const parts = header.split(";");
  for (const part of parts) {
    const trimmed = part.trim();
    if (trimmed.startsWith(`${name}=`)) {
      return trimmed.slice(name.length + 1);
    }
  }
  return void 0;
}
function resolveTenantOverride(raw) {
  if (raw === void 0) return { applied: false, value: null };
  const decoded = decodeCookieValue(raw);
  if (!decoded) return { applied: false, value: null };
  const trimmed = decoded.trim();
  if (!trimmed) return { applied: false, value: null };
  return { applied: true, value: trimmed };
}
function resolveOrganizationOverride(raw) {
  if (raw === void 0) return { applied: false, value: null };
  const decoded = decodeCookieValue(raw);
  if (!decoded || decoded === ALL_ORGANIZATIONS_COOKIE_VALUE) {
    return { applied: true, value: null };
  }
  const trimmed = decoded.trim();
  if (!trimmed || trimmed === ALL_ORGANIZATIONS_COOKIE_VALUE) {
    return { applied: true, value: null };
  }
  return { applied: true, value: trimmed };
}
function isSuperAdminAuth(auth) {
  if (!auth) return false;
  return auth.isSuperAdmin === true;
}
function applySuperAdminScope(auth, tenantCookie, orgCookie) {
  if (!auth || !isSuperAdminAuth(auth)) return auth;
  const tenantOverride = resolveTenantOverride(tenantCookie);
  const orgOverride = resolveOrganizationOverride(orgCookie);
  if (!tenantOverride.applied && !orgOverride.applied) return auth;
  const baseAuth = auth;
  const next = { ...baseAuth };
  if (tenantOverride.applied) {
    if (!("actorTenantId" in next)) next.actorTenantId = auth?.tenantId ?? null;
    next.tenantId = tenantOverride.value;
  }
  if (orgOverride.applied) {
    if (!("actorOrgId" in next)) next.actorOrgId = auth?.orgId ?? null;
    next.orgId = orgOverride.value;
  }
  next.isSuperAdmin = true;
  const existingRoles = Array.isArray(next.roles) ? next.roles : [];
  if (!existingRoles.some((role) => typeof role === "string" && role.trim().toLowerCase() === SUPERADMIN_ROLE)) {
    next.roles = [...existingRoles, "superadmin"];
  }
  return next;
}
async function resolveApiKeyAuth(secret) {
  if (!secret) return null;
  const cache = getSharedApiKeyAuthCache();
  const cached = cache.get(secret);
  if (cached !== void 0) return cached;
  try {
    const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
    const container = await createRequestContainer();
    const em = container.resolve("em");
    const { findApiKeyBySecret } = await import("@open-mercato/core/modules/api_keys/services/apiKeyService");
    const { Role, RoleAcl, User } = await import("@open-mercato/core/modules/auth/data/entities");
    const { Organization, Tenant } = await import("@open-mercato/core/modules/directory/data/entities");
    const record = await findApiKeyBySecret(em, secret);
    if (!record) {
      cache.setMiss(secret);
      return null;
    }
    const roleIds = Array.isArray(record.rolesJson) ? record.rolesJson.filter((value) => typeof value === "string" && value.length > 0) : [];
    const roles = roleIds.length ? await em.find(Role, { id: { $in: roleIds }, deletedAt: null }) : [];
    const roleNames = roles.map((role) => role.name).filter((name) => typeof name === "string" && name.length > 0);
    let keyIsSuperAdmin = false;
    const keyOrganizationId = typeof record.organizationId === "string" && record.organizationId.trim().length > 0 ? record.organizationId.trim() : null;
    if (roleIds.length && !keyOrganizationId) {
      const superAcls = await em.find(
        RoleAcl,
        {
          role: { $in: roleIds },
          tenantId: record.tenantId ?? null,
          isSuperAdmin: true,
          deletedAt: null
        }
      );
      keyIsSuperAdmin = superAcls.some((acl) => {
        const organizations = Array.isArray(acl.organizationsJson) ? acl.organizationsJson : null;
        return !organizations || organizations.length === 0 || organizations.includes("__all__");
      });
    }
    if (cache.shouldWriteLastUsed(record.id)) {
      try {
        record.lastUsedAt = /* @__PURE__ */ new Date();
        await em.persist(record).flush();
      } catch {
      }
    }
    const isSessionBoundKey = Boolean(
      record.sessionToken || record.sessionUserId || record.sessionSecretEncrypted || record.opencodeSessionId
    );
    const actualUserId = isSessionBoundKey ? record.sessionUserId ?? null : record.createdBy ?? null;
    if (isSessionBoundKey && !actualUserId) {
      cache.setMiss(secret);
      return null;
    }
    if (actualUserId) {
      const user = await em.findOne(User, { id: actualUserId, deletedAt: null });
      if (!user) {
        cache.setMiss(secret);
        return null;
      }
      if ((user.tenantId ?? null) !== (record.tenantId ?? null)) {
        cache.setMiss(secret);
        return null;
      }
      const requiresExactOrganization = isSessionBoundKey || Boolean(record.organizationId);
      if (requiresExactOrganization && (user.organizationId ?? null) !== (record.organizationId ?? null)) {
        cache.setMiss(secret);
        return null;
      }
    } else {
      if (record.tenantId) {
        const tenant = await em.findOne(Tenant, { id: record.tenantId, deletedAt: null, isActive: true });
        if (!tenant) {
          cache.setMiss(secret);
          return null;
        }
      }
      if (record.organizationId) {
        const organization = await em.findOne(Organization, { id: record.organizationId, deletedAt: null, isActive: true });
        if (!organization) {
          cache.setMiss(secret);
          return null;
        }
        if (record.tenantId && String(organization.tenant.id) !== record.tenantId) {
          cache.setMiss(secret);
          return null;
        }
      }
    }
    const auth = {
      sub: `api_key:${record.id}`,
      tenantId: record.tenantId ?? null,
      orgId: record.organizationId ?? null,
      roles: roleNames,
      isApiKey: true,
      isSuperAdmin: keyIsSuperAdmin,
      keyId: record.id,
      keyName: record.name,
      ...actualUserId ? { userId: actualUserId } : {}
    };
    cache.setSuccess(secret, auth, record.expiresAt ? record.expiresAt.getTime() : null);
    return auth;
  } catch (err) {
    if (isTransientDbError(err)) {
      throw new AuthResolutionUnavailableError(err);
    }
    return null;
  }
}
function extractApiKey(req) {
  const header = (req.headers.get("x-api-key") || "").trim();
  if (header) return header;
  const authHeader = (req.headers.get("authorization") || "").trim();
  if (authHeader.toLowerCase().startsWith("apikey ")) {
    return authHeader.slice(7).trim();
  }
  return null;
}
async function resolveCanonicalInteractiveAuthContext(auth) {
  if (!auth || auth.isApiKey) return auth;
  if (typeof auth.sub !== "string" || auth.sub.trim().length === 0) return null;
  try {
    const [{ createRequestContainer }, { resolveCanonicalStaffAuthContext }] = await Promise.all([
      import("@open-mercato/shared/lib/di/container"),
      import("@open-mercato/core/modules/auth/lib/sessionIntegrity")
    ]);
    const container = await createRequestContainer();
    const em = container.resolve("em");
    return await resolveCanonicalStaffAuthContext(em, auth);
  } catch (err) {
    throw new AuthResolutionUnavailableError(err);
  }
}
async function resolveAuthFromCookiesDetailed() {
  const cookieStore = await cookies();
  const token = cookieStore.get("auth_token")?.value;
  if (!token) return { auth: null, status: "missing" };
  try {
    const payload = verifyJwt(token);
    if (!payload) return { auth: null, status: "invalid" };
    if (payload.type === "customer") return { auth: null, status: "invalid" };
    const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload);
    if (!canonicalAuth) return { auth: null, status: "invalid" };
    const tenantCookie = cookieStore.get(TENANT_COOKIE_NAME)?.value;
    const orgCookie = cookieStore.get(ORGANIZATION_COOKIE_NAME)?.value;
    return {
      auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),
      status: "authenticated"
    };
  } catch (err) {
    if (err instanceof AuthResolutionUnavailableError) return { auth: null, status: "error" };
    return { auth: null, status: "invalid" };
  }
}
async function getAuthFromCookies() {
  return (await resolveAuthFromCookiesDetailed()).auth;
}
async function resolveAuthFromRequestDetailed(req) {
  const trusted = readTrustedAuthContext(req);
  if (trusted) {
    return {
      auth: trusted.auth,
      status: trusted.status ?? (trusted.auth ? "authenticated" : "missing")
    };
  }
  const cookieHeader = req.headers.get("cookie") || "";
  const tenantCookie = readCookieFromHeader(cookieHeader, TENANT_COOKIE_NAME);
  const orgCookie = readCookieFromHeader(cookieHeader, ORGANIZATION_COOKIE_NAME);
  const authHeader = (req.headers.get("authorization") || "").trim();
  let token;
  let hadInvalidInteractiveToken = false;
  let hadUnavailableResolution = false;
  if (authHeader.toLowerCase().startsWith("bearer ")) token = authHeader.slice(7).trim();
  if (!token) {
    const match = cookieHeader.match(/(?:^|;\s*)auth_token=([^;]+)/);
    if (match) token = decodeURIComponent(match[1]);
  }
  if (token) {
    try {
      const payload = verifyJwt(token);
      if (payload && payload.type === "customer") return { auth: null, status: "invalid" };
      if (payload) {
        const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload);
        if (canonicalAuth) {
          return {
            auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),
            status: "authenticated"
          };
        }
        hadInvalidInteractiveToken = true;
      }
    } catch (err) {
      if (err instanceof AuthResolutionUnavailableError) hadUnavailableResolution = true;
      else hadInvalidInteractiveToken = true;
    }
  }
  const resolveUnauthenticatedStatus = () => hadUnavailableResolution ? "error" : hadInvalidInteractiveToken ? "invalid" : "missing";
  const apiKey = extractApiKey(req);
  if (!apiKey) {
    return { auth: null, status: resolveUnauthenticatedStatus() };
  }
  let apiAuth;
  try {
    apiAuth = await resolveApiKeyAuth(apiKey);
  } catch (err) {
    if (!(err instanceof AuthResolutionUnavailableError)) {
      throw err;
    }
    hadUnavailableResolution = true;
    return { auth: null, status: resolveUnauthenticatedStatus() };
  }
  if (!apiAuth) {
    return { auth: null, status: resolveUnauthenticatedStatus() };
  }
  return {
    auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),
    status: "authenticated"
  };
}
async function getAuthFromRequest(req) {
  return (await resolveAuthFromRequestDetailed(req)).auth;
}
export {
  AuthResolutionUnavailableError,
  TRUSTED_AUTH_CONTEXT_SYMBOL,
  attachTrustedAuthContext,
  getAuthFromCookies,
  getAuthFromRequest,
  resolveAuthFromCookiesDetailed,
  resolveAuthFromRequestDetailed
};
//# sourceMappingURL=server.js.map
