import type { ActivityTenant } from "../ports/audit.js";
import { type RequireOptions, requireTenant } from "../ports/auth.js";

const tenantScopeBrand = Symbol("TenantScope");

/**
 * Branded tenant scope token for repository boundaries.
 *
 * Apps still own tenant resolution and tenant data modeling. `TenantScope`
 * carries the already-resolved activity tenant through app-facing repository
 * ports so adapters can apply tenant predicates without accepting arbitrary
 * caller-provided tenant IDs.
 */
export type TenantScope = Readonly<{
  id: string;
  tenant: ActivityTenant;
  readonly [tenantScopeBrand]: true;
}>;

/**
 * Create a tenant scope from a resolved activity tenant.
 *
 * This does not load, create, or validate a tenant record. It only brands the
 * already-resolved tenant for repository and adapter boundaries.
 */
export function createTenantScope(tenant: ActivityTenant): TenantScope {
  return {
    id: tenant.id,
    tenant,
    [tenantScopeBrand]: true,
  };
}

/**
 * Return a branded tenant scope from `ctx.tenant` or throw.
 *
 * Throws `TenantRequiredError` by default through `requireTenant`. Pass
 * `options.error` to throw an app-owned error instead.
 */
export function requireTenantScope(
  ctx: { tenant?: ActivityTenant | null },
  options?: RequireOptions,
): TenantScope {
  return createTenantScope(requireTenant(ctx, options));
}

/**
 * Extract the stable tenant ID from a tenant scope.
 *
 * Repository adapters should use this helper at the persistence boundary
 * instead of accepting raw tenant IDs from use cases.
 */
export function tenantScopeId(scope: TenantScope): string {
  return scope.id;
}
