import { tryNormalizeHostname } from "@open-mercato/core/modules/customer_accounts/lib/hostname";
import { platformDomains } from "@open-mercato/core/modules/customer_accounts/lib/platformDomains";
import { secretEqual } from "@open-mercato/core/modules/customer_accounts/lib/secretCompare";
import { Organization } from "@open-mercato/core/modules/directory/data/entities";
class TenantResolutionError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
    this.name = "TenantResolutionError";
  }
}
function readForcedHost(req) {
  if (process.env.NODE_ENV !== "test") return null;
  const expected = process.env.FORCE_HOST_SECRET;
  if (!expected) return null;
  if (!secretEqual(req.headers.get("x-force-host-secret"), expected)) return null;
  const host = req.headers.get("x-force-host");
  return host && host.length > 0 ? host : null;
}
async function resolveTenantFromOrganization(organizationId, container) {
  let em;
  if (container) {
    em = container.resolve("em");
  } else {
    const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
    const requestContainer = await createRequestContainer();
    em = requestContainer.resolve("em");
  }
  const organization = await em.findOne(
    Organization,
    { id: organizationId, deletedAt: null },
    { populate: ["tenant"] }
  );
  if (!organization) {
    throw new TenantResolutionError("Organization not found", 400);
  }
  const tenantId = typeof organization.tenant === "string" ? organization.tenant : organization.tenant?.id ? String(organization.tenant.id) : null;
  if (!tenantId) {
    throw new TenantResolutionError("Organization not found", 400);
  }
  return { tenantId, organizationId: String(organization.id) };
}
async function resolveTenantContext(req, bodyTenantId, options) {
  const rawHost = readForcedHost(req) ?? req.headers.get("host");
  const hostname = rawHost ? tryNormalizeHostname(rawHost) : null;
  const isPlatform = hostname ? platformDomains().includes(hostname) : true;
  const bodyOrganizationId = options?.organizationId ?? null;
  if (isPlatform) {
    if (bodyOrganizationId) {
      const resolved2 = await resolveTenantFromOrganization(bodyOrganizationId, options?.container);
      if (bodyTenantId && bodyTenantId !== resolved2.tenantId) {
        throw new TenantResolutionError(
          "tenantId in request body does not match the resolved organization",
          400
        );
      }
      return {
        source: "body",
        tenantId: resolved2.tenantId,
        organizationId: resolved2.organizationId,
        hostname
      };
    }
    if (!bodyTenantId) {
      throw new TenantResolutionError(
        "organizationId or tenantId is required for platform-domain logins",
        400
      );
    }
    return { source: "body", tenantId: bodyTenantId, organizationId: null, hostname };
  }
  let service = null;
  try {
    if (options?.container) {
      service = options.container.resolve("domainMappingService");
    } else {
      const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
      const container = await createRequestContainer();
      service = container.resolve("domainMappingService");
    }
  } catch {
    throw new TenantResolutionError("Custom domain routing is not available on this deployment", 503);
  }
  if (!hostname) throw new TenantResolutionError("Unable to determine request hostname", 400);
  const resolved = await service.resolveByHostname(hostname);
  if (!resolved || resolved.status !== "active") {
    throw new TenantResolutionError("This domain is not configured for any active organization", 404);
  }
  if (bodyTenantId && bodyTenantId !== resolved.tenantId) {
    throw new TenantResolutionError(
      "tenantId in request body does not match the resolved custom domain",
      400
    );
  }
  return {
    source: "host",
    tenantId: resolved.tenantId,
    organizationId: resolved.organizationId,
    hostname
  };
}
export {
  TenantResolutionError,
  resolveTenantContext
};
//# sourceMappingURL=resolveTenantContext.js.map
