import type { AuthzModule } from "./types.ts";

const PERMISSION_ID = /^[a-z0-9]+(?::[a-z0-9-]+)+$/;

/**
 * Validate and freeze one authorization declaration. Called from
 * `app/authz/<name>.ts` as the module's default export.
 */
export function defineAuthz(module: AuthzModule): AuthzModule {
  const permissions = module.permissions ?? {};
  const roles = module.roles ?? {};
  const policies = module.policies ?? {};
  const attributes = module.attributes ?? {};
  const bindings = module.bindings ?? {};

  for (const id of Object.keys(permissions)) {
    if (id.includes("*")) {
      throw new Error(
        `WRN-AUTHZ-DECL: permission id '${id}' must not contain a wildcard; wildcards belong in roles.`,
      );
    }
    if (!PERMISSION_ID.test(id)) {
      throw new Error(
        `WRN-AUTHZ-DECL: permission id '${id}' must be lowercase colon-namespaced, e.g. 'post:read'.`,
      );
    }
  }

  for (const [role, grants] of Object.entries(roles)) {
    for (const grant of grants) {
      if (typeof grant !== "string" || !grant.trim()) {
        throw new Error(
          `WRN-AUTHZ-DECL: role '${role}' grants an empty entry; expected a permission, 'ns:*', or 'role:<name>'.`,
        );
      }
    }
  }

  for (const [permission, names] of Object.entries(bindings)) {
    for (const name of names) {
      if (!(name in policies)) {
        throw new Error(
          `WRN-AUTHZ-DECL: binding for '${permission}' names policy '${name}', which is not declared in the same module.`,
        );
      }
    }
  }

  return Object.freeze({ permissions, roles, policies, attributes, bindings });
}
