/**
 * The bounded-delegation enforcement checkpoint (ADR 0007 §5).
 *
 * The chain owner caps every registered link's verdict so a buggy or over-eager
 * external judge can never exceed the operator's policy: a link's `allow` on an
 * excluded surface is downgraded to `defer`, letting the `ask` fall through to
 * the terminal (a prompt) instead. The checkpoint only ever *tightens* a
 * verdict — it never turns a `defer`/`deny` into an `allow`.
 *
 * The excluded set is the whole `path` surface plus `external_directory`, with
 * one bundle-maintained exception: the built-in `read` tool may accept a link's
 * `allow` for an external-directory ask. Mutating tools, bash, extension tools,
 * and unknown tools remain capped to the terminal human authority. A finer
 * secret-shaped-`path` exclusion remains deferred; `path` stays fully excluded.
 */

import type { Authorizer } from "./authorizer";
import type { PromptPermissionDetails } from "./permission-prompter";

/** Surfaces on which a link may never grant an `allow` (ADR 0007 §5). */
export const DELEGATION_EXCLUDED_SURFACES: ReadonlySet<string> = new Set([
  "external_directory",
  "path",
]);

/** Read-only external-directory access explicitly delegable in my-pi. */
const DELEGABLE_EXTERNAL_DIRECTORY_TOOLS: ReadonlySet<string> = new Set(["read"]);

/**
 * Wrap a link's `authorize` so an `allow` on an excluded surface is capped to
 * `defer`. All other verdicts, and `allow`s on non-excluded surfaces, pass
 * through unchanged. `details`, the injected `query`, and the review-log `log`
 * are forwarded as-is.
 */
export function encloseInDelegationEnvelope(
  authorize: Authorizer["authorize"],
): Authorizer["authorize"] {
  return async (details, query, log) => {
    const verdict = await authorize(details, query, log);
    if (verdict.kind === "allow" && isExcludedSurface(details)) {
      return { kind: "defer" };
    }
    return verdict;
  };
}

/**
 * Whether an allow verdict exceeds the delegation envelope. The gate-computed
 * surface is authoritative. `path` remains fully excluded; external-directory
 * access is excluded unless it comes from the built-in `read` tool. Unknown
 * surfaces fail safe to the terminal authority.
 */
function isExcludedSurface(details: PromptPermissionDetails): boolean {
  const surface = details.accessIntent?.surface ?? details.surface ?? undefined;
  if (surface === undefined) {
    return true;
  }
  if (surface === "external_directory") {
    return !(
      details.toolName !== undefined &&
      DELEGABLE_EXTERNAL_DIRECTORY_TOOLS.has(details.toolName)
    );
  }
  return DELEGATION_EXCLUDED_SURFACES.has(surface);
}
