/**
 * User ↔ Person bridge
 *
 * Resolves between auth.db user accounts and person entities. The pointer
 * lives on the person side as `user_handle` (scalar handle, not a wikilink —
 * users are out-of-graph identities). Workspace-unique: at most one person
 * entity per auth handle, workspace-wide.
 *
 * The lookup primitives here are intentionally cheap (in-memory scan of
 * already-loaded person entities). No persistent cache in v1 — if profiling
 * later shows this is hot, add one with explicit invalidation on entity
 * writes and on createUser/deleteUser.
 */
import type { WorkspaceManager } from '../core/workspace-manager.js';
import { type AuthService, type AuthUser } from './auth-service.js';
import type { GitUser } from './git.js';
/** Stable reference to a person entity in the workspace graph. */
export interface PersonRef {
    repo: string;
    entityId: string;
}
/** Reason codes returned by `assertUserHandleUnique` / linking flows. */
export type BridgeErrorCode = 'USER_HANDLE_CONFLICT' | 'USER_HANDLE_UNKNOWN' | 'PERSON_ALREADY_LINKED' | 'PERSON_NOT_FOUND' | 'OWNER_REQUIRED';
export declare class BridgeError extends Error {
    readonly code: BridgeErrorCode;
    readonly details?: Record<string, unknown> | undefined;
    constructor(code: BridgeErrorCode, message: string, details?: Record<string, unknown> | undefined);
}
/**
 * Forward lookup: given an auth user handle, return the person entity that
 * represents them. Returns null when the user has no linked entity (legacy,
 * unprovisioned, or explicitly unlinked).
 */
export declare function findPersonRefForUser(workspace: WorkspaceManager, userHandle: string): PersonRef | null;
/**
 * Reverse lookup: given a person-entity target ID (the wikilink target, e.g.
 * `fritz` from `[[fritz]]`), return the auth user that entity is linked to.
 * Returns null when the entity isn't linked, doesn't exist, or refers to a
 * non-existent user (pre-link case).
 */
export declare function findUserForPersonRef(workspace: WorkspaceManager, authService: AuthService, targetId: string): AuthUser | null;
/**
 * Bulk forward map: handle → personRef for every linked person in the
 * workspace. Useful for `list_workspace_users` and for migration reporting.
 */
export declare function getHandleToPersonRefMap(workspace: WorkspaceManager): ReadonlyMap<string, PersonRef>;
/**
 * Workspace-wide uniqueness check. Throws BridgeError(USER_HANDLE_CONFLICT)
 * when another person entity already carries `userHandle`. The `exceptRef`
 * lets update paths skip the check for the entity being written.
 *
 * Called from the write path (graph-api create/update, agent graph-tools
 * create/update) before delegating to graph.create/update.
 */
export declare function assertUserHandleUnique(workspace: WorkspaceManager, userHandle: string, exceptRef?: PersonRef): void;
/**
 * Authz gate: only workspace owners can stamp or change `user_handle` on a
 * person entity. Mirrors the `ownerOnly()` gate on the dedicated
 * `link_person_to_user` / `unlink_person_from_user` agent tools — without
 * this, a non-owner member with folder-write access could rewrite the
 * bridge via the generic update_entity REST/agent path, "claiming"
 * another user's auth identity through the agent's Current User prompt.
 *
 * Rules:
 *   - Payload doesn't include `user_handle` → no-op (caller wasn't trying
 *     to change the bridge).
 *   - Payload's value equals the current stored value → no-op (a form
 *     resubmit including the unchanged field shouldn't 403).
 *   - Caller is a workspace owner → allowed.
 *   - Anyone else → throw OWNER_REQUIRED.
 *
 * Pass `currentHandle = ''` for create paths (no prior entity).
 */
export declare function assertCanChangeUserHandle(authUser: AuthUser | null | undefined, currentHandle: string, newHandle: unknown): void;
/**
 * Inspect a frontmatter payload for `user_handle` and, if present, validate
 * uniqueness against the current workspace state. No-op when the field is
 * absent or empty.
 *
 * `exceptRef` is the entity being written (for updates) so the check
 * doesn't reject an entity for clashing with itself.
 */
export declare function validateUserHandleInPayload(workspace: WorkspaceManager, data: Record<string, unknown>, exceptRef?: PersonRef): void;
/**
 * Map an AuthUser to a GitUser for commit attribution. Mirrors `reqGitUser`
 * in graph-api-access.ts — kept here so the registration / CLI sites
 * (which don't have a Fastify request) can call into the bridge without
 * importing the request-scoped helper.
 */
export declare function authUserToGitUser(user: AuthUser): GitUser;
/** Result returned by `ensurePersonForUser` so callers can log/UX the action. */
export type EnsurePersonResult = {
    action: 'already-linked';
    ref: PersonRef;
} | {
    action: 'linked';
    ref: PersonRef;
} | {
    action: 'created';
    ref: PersonRef;
} | {
    action: 'skipped';
    reason: string;
};
/**
 * Resolve a person entity for a freshly-registered (or existing) user.
 *
 *   1. If the user is already linked, return.
 *   2. If a person entity carries `email === user.email` and no `user_handle`,
 *      stamp the handle onto it ("linked" — preserves the user-edited record).
 *   3. Otherwise auto-create a person entity in the workspace's team repo
 *      with the user's name/email and the new handle.
 *
 * Returns `{ action: 'skipped' }` when no suitable team repo exists and no
 * email-matchable entity is available — the caller can warn the operator
 * but should never block user creation on this.
 */
export declare function ensurePersonForUser(workspace: WorkspaceManager, authService: AuthService, authUser: AuthUser, gitUser: GitUser): Promise<EnsurePersonResult>;
/**
 * Self-service link: stamp `authUser.handle` onto a SPECIFIC person entity the
 * user picked (the home "which record is you?" picker). Unlike the owner-gated
 * `link_person_to_user` (which can target ANY user's account), this only ever
 * writes the CALLER's OWN handle onto an UNLINKED person — so it's safe without
 * an owner gate: it can't claim another user's identity (their record already
 * carries a handle → rejected) and confers no privileges (auth role comes from
 * the account, not the entity).
 *
 * Throws `BridgeError`:
 *   - `PERSON_ALREADY_LINKED` — the target already carries a different handle.
 *   - `USER_HANDLE_CONFLICT`  — the caller's handle is already on another person.
 * No-op when the target already carries the caller's handle (idempotent).
 *
 * The caller resolves `ref` (including read-access) and the "already linked
 * elsewhere" precheck before calling.
 */
export declare function linkChosenPersonToUser(workspace: WorkspaceManager, authUser: AuthUser, ref: PersonRef, gitUser: GitUser): Promise<void>;
