/**
 * Raw queries against the `cell` table.
 *
 * Convention: `deps: QueryDeps` first, no audit side effects, mutations
 * return the affected row (or `null` for not-found).
 *
 * `cell.refs` is auto-extracted from `data` on every create and update via
 * `fact_hash_extract_refs` (depth-first walk for `blake3:`-prefixed strings). Callers
 * never pass `refs` directly — the column is a derived projection of
 * `data` for cells-by-fact discovery, mirroring what a fact store does for
 * JSON facts.
 *
 * Soft delete via `deleted_at`. All `get` / `list` queries exclude
 * tombstones by default; `include_deleted: true` opts in for admin /
 * audit views.
 *
 * `path` uniqueness is global, enforced by `idx_cell_path_unique` (partial
 * on active rows). Path reuse after soft delete falls out of the partial
 * index — queries do not need special handling.
 *
 * @module
 */
import type { QueryDeps } from './query_deps.js';
import type { Json } from '@fuzdev/fuz_util/json.js';
import type { Uuid } from '@fuzdev/fuz_util/id.js';
import { type FactHash } from '@fuzdev/fuz_util/fact_hash.js';
import type { CellData } from '../auth/cell_data_schema.js';
import type { CellVisibility } from '../auth/cell_action_specs.js';
/**
 * Row shape returned by `cell` SELECTs. `data` is typed as `CellData` —
 * the storage layer trusts the wire validation; the row is what was
 * written, and the wire validates `CellData` on every write.
 *
 * Parent↔child membership and named relations live in the `cell_item` /
 * `cell_field` sibling tables (see `cell_item_queries.ts` /
 * `cell_field_queries.ts`). The cell row carries identity + content only.
 *
 * `grant_count` is a derived projection (correlated subquery against
 * `cell_grant` keyed by `cell_id`, served by `idx_cell_grant_cell`) —
 * not a table column. New cells naturally land at 0.
 */
export interface CellRow {
    id: Uuid;
    data: CellData;
    visibility: CellVisibility;
    path: string | null;
    refs: Array<FactHash> | null;
    created_at: Date;
    updated_at: Date | null;
    deleted_at: Date | null;
    created_by: Uuid | null;
    updated_by: Uuid | null;
    grant_count: number;
}
/** Input for `query_cell_create`. `refs` is derived from `data`. */
export interface CellCreateQueryInput {
    data: Json;
    visibility?: CellVisibility;
    path?: string | null;
    created_by?: Uuid | null;
}
/**
 * Patch for `query_cell_update`. Fields left `undefined` are unchanged;
 * `path` may be explicitly set to `null` to clear. `refs` is re-derived
 * from `data` whenever `data` is updated.
 */
export interface CellUpdatePatch {
    data?: Json;
    visibility?: CellVisibility;
    path?: string | null;
    updated_by?: Uuid | null;
}
/** Common pagination + tombstone-visibility options for list queries. */
export interface CellListOptions {
    limit?: number;
    offset?: number;
    include_deleted?: boolean;
}
/**
 * Insert a cell row, deriving `refs` from `data`.
 *
 * `updated_by` is left NULL on insert — same convention as `updated_at`
 * (NULL until first update). The "last modifier" stamp is meaningful only
 * after a real edit; copying the creator's id into `updated_by` at create
 * time would make a no-op update by a different actor look authored by
 * the creator.
 *
 * @param deps - query deps
 * @param input - data, optional visibility, path, and ownership
 * @returns the inserted row
 * @mutates `cell` - inserts one row
 */
export declare const query_cell_create: (deps: QueryDeps, input: CellCreateQueryInput) => Promise<CellRow>;
/**
 * Fetch a cell by id. Excludes soft-deleted rows by default.
 *
 * @param deps - query deps
 * @param id - cell id
 * @param options - `include_deleted: true` returns tombstones
 * @returns the row or `null` when not found (or soft-deleted and not requested)
 */
export declare const query_cell_get: (deps: QueryDeps, id: Uuid, options?: {
    include_deleted?: boolean;
}) => Promise<CellRow | null>;
/**
 * Fetch a cell by `path`. Excludes soft-deleted rows; the global partial
 * unique index on `path WHERE deleted_at IS NULL` guarantees at most one
 * result.
 *
 * @param deps - query deps
 * @param path - the named lookup alias (e.g. `/map/main`)
 * @returns the row or `null` when not found
 */
export declare const query_cell_get_by_path: (deps: QueryDeps, path: string) => Promise<CellRow | null>;
/**
 * Bulk-load active cell rows by id, **no visibility filter applied**. Used
 * by the strict relation-read filter (`auth/cell_relation_visibility.ts`'s
 * `filter_visible_target_ids`), which runs `can_view_cell` per row in
 * memory rather than in SQL. Soft-deleted rows are excluded so relations
 * to tombstones never surface.
 *
 * @param deps - query deps
 * @param ids - cell ids to load (duplicates are harmless)
 * @returns active rows in arbitrary order (caller indexes by `id`)
 */
export declare const query_cell_load_many: (deps: QueryDeps, ids: ReadonlyArray<Uuid>) => Promise<Array<CellRow>>;
/**
 * Update a cell. Fields left `undefined` in the patch keep their existing
 * value; explicit `null` writes `NULL`. `refs` is re-derived from `data`
 * whenever the patch updates `data`. `updated_at` is bumped to `NOW()`
 * on every successful update.
 *
 * @param deps - query deps
 * @param id - cell id
 * @param patch - subset of mutable fields
 * @returns the updated row, or `null` when no row matched (already deleted
 *   or never existed)
 * @mutates `cell` - updates one row
 */
export declare const query_cell_update: (deps: QueryDeps, id: Uuid, patch: CellUpdatePatch) => Promise<CellRow | null>;
/**
 * Soft-delete a cell. Sets `deleted_at = NOW()`, `updated_at = NOW()`,
 * and `updated_by = options.deleted_by` (or `NULL`). No-op when the row
 * is already deleted.
 *
 * @param deps - query deps
 * @param id - cell id
 * @param options - `deleted_by` records who triggered the delete
 * @returns `true` when a row was soft-deleted, `false` when no active row matched
 * @mutates `cell` - sets `deleted_at` on one row
 */
export declare const query_cell_delete: (deps: QueryDeps, id: Uuid, options?: {
    deleted_by?: Uuid | null;
}) => Promise<boolean>;
/**
 * List cells whose `data.kind` matches the given value, newest first.
 * Uses the `idx_cell_data` GIN index (`data @> ...`).
 *
 * @param deps - query deps
 * @param kind - `data.kind` value to match (e.g. `'collection'`, `'entry'`)
 * @param options - pagination
 * @returns matching active rows
 */
export declare const query_cell_list_by_data_kind: (deps: QueryDeps, kind: string, options?: Pick<CellListOptions, "limit" | "offset">) => Promise<Array<CellRow>>;
/**
 * List active cells created by an actor, newest first. Backed by the
 * `idx_cell_created_by` partial index.
 *
 * @param deps - query deps
 * @param actor_id - the creator's actor id
 * @param options - pagination
 * @returns matching active rows
 */
export declare const query_cell_list_by_creator: (deps: QueryDeps, actor_id: Uuid, options?: Pick<CellListOptions, "limit" | "offset">) => Promise<Array<CellRow>>;
/**
 * Filterable list query for the generic `cell_list` RPC.
 *
 * Takes a flat filter shape (single optional clause per dimension; the
 * `cell_list` API explicitly does NOT support OR'd alternatives within a
 * dimension — keep it simple) plus an optional viewer-aware visibility
 * predicate.
 *
 * The visibility predicate mirrors `can_view_cell` in SQL form:
 *
 * ```
 * (viewer_is_admin
 *  OR cell.visibility = 'public'
 *  OR (viewer_actor_id IS NOT NULL AND created_by = viewer_actor_id)
 *  OR (viewer_actor_id IS NOT NULL AND <grant admits caller>))
 * ```
 *
 * The grants branch closes parity with `can_view_cell`: a SQL `EXISTS`
 * over `cell_grant`, parameterized by the caller's `actor_id` and the
 * parallel `(role[], scope_id[])` projection of `auth.role_grants`. The
 * caller's role_grants are materialized once via a `caller_role_grants`
 * CTE so the role-grant `unnest` isn't re-scanned per outer row. Empty
 * role_grant arrays are fine: the CTE yields zero rows, the inner EXISTS
 * returns false, and the actor-grant branch still fires for actor-shaped
 * grants.
 *
 * `shared_with_caller_only: true` (`shared_with: 'me'` at the wire layer)
 * takes a **different SQL shape**: instead of layering an extra
 * conjunction on the cell-driven scan, it semi-joins through
 * `cell_grant`, letting the planner drive from the (typically tiny)
 * admitted-grant set via `idx_cell_grant_actor` /
 * `idx_cell_grant_role_scope` rather than scanning every cell row. For a
 * sharee with N grants over a table of M cells, the cost drops from
 * O(M) to O(N + matched-cells). Owner-is-implicit (a cell's owner never
 * appears as a grant principal) means the grants branch is itself
 * owner-excluding, but the explicit `created_by IS DISTINCT FROM caller`
 * guards against any future deviation. The shared_with branch does NOT
 * bypass for admin: an admin asking "what's shared with me" wants their
 * own grant footprint, not every cell.
 *
 * Soft-deleted rows are excluded by default; opt-in via `include_deleted`.
 *
 * @param deps - query deps
 * @param params - filter + visibility + ordering + pagination
 * @returns matching rows, ordered per `order_by` / `order_direction`
 */
export declare const query_cell_list: (deps: QueryDeps, params: CellListParams) => Promise<Array<CellRow>>;
/** Parameters for `query_cell_list`. All filter dimensions are optional. */
export interface CellListParams {
    /** Match `data.kind = ?` via `data @> {"kind": ?}` (uses `idx_cell_data`). */
    data_kind?: string;
    /**
     * Match `cell.visibility = ?` directly on the top-level column.
     * Additional narrowing on top of the SQL-side auth visibility
     * predicate — useful for the public discovery feed where authed
     * callers must NOT see their own private entries mixed in.
     */
    visibility?: CellVisibility;
    /** Match cells whose `refs[]` contains this hash (uses `idx_cell_refs`). */
    ref?: FactHash;
    /** Filter to cells created by this actor (uses `idx_cell_created_by`). */
    created_by?: Uuid;
    /**
     * Filter to cells whose `path` starts with this prefix. Wildcard
     * metachars in the prefix are NOT special — `starts_with()` does
     * literal matching.
     */
    path_prefix?: string;
    /**
     * Batch-fetch by id. The visibility predicate still runs, so callers
     * passing ids they can't view simply get fewer rows back. Order of
     * the returned rows follows `order_by` / `order_direction`, not the
     * input list — callers that need positional output (e.g. preserving
     * a collection's `items[]` order) should re-index client-side.
     */
    ids?: Array<Uuid>;
    /**
     * Viewer actor for the visibility predicate. Pass `null` for
     * unauthenticated callers — only `cell.visibility === 'public'` rows
     * are admitted then.
     */
    viewer_actor_id: Uuid | null;
    /**
     * When `true`, the visibility predicate is dropped (admin sees all).
     * When `false`, rows pass when public, owned by the viewer, or
     * admitted by a `cell_grant` row.
     */
    viewer_is_admin: boolean;
    /**
     * Caller's `actor_id` for the actor-shaped grant branch. NULL =
     * anonymous (actor-grants can never admit). Kept distinct from
     * `viewer_actor_id` for the predicate's clarity (the visibility branch
     * and the grant branch are independent concerns even when they
     * currently agree).
     */
    caller_actor_id?: Uuid | null;
    /**
     * Caller's role_grant roles, parallel-array projection of `auth.role_grants`
     * (active-only — middleware filters). Pair-wise aligned with
     * `caller_role_grant_scope_ids`. Empty array (or omitted) admits no
     * role-shaped grants. The two arrays MUST have equal length —
     * `unnest(text[], uuid[])` null-pads on length mismatch and would
     * silently widen role-grant admits.
     */
    caller_role_grant_roles?: ReadonlyArray<string>;
    /**
     * Caller's role_grant scope ids, parallel-array projection. NULLs in the
     * array mark global (any-scope) role_grants — `IS NOT DISTINCT FROM`
     * handles them per design.
     */
    caller_role_grant_scope_ids?: ReadonlyArray<Uuid | null>;
    /**
     * When `true`, narrow to cells admitting the caller via a
     * `cell_grant` row AND that the caller does not own. Authenticated
     * only (`viewer_actor_id` must be set). Combine with `data_kind` /
     * `path_prefix` etc. to scope further.
     */
    shared_with_caller_only?: boolean;
    /** Sort column. Default `created_at`. */
    order_by?: 'created_at' | 'updated_at';
    /** Sort direction. Default `desc`. */
    order_direction?: 'asc' | 'desc';
    /** Page size. */
    limit?: number;
    /** Page offset. */
    offset?: number;
    /** Include soft-deleted rows. Default `false`. */
    include_deleted?: boolean;
}
/**
 * List active cells whose `refs` array contains the given fact hash,
 * newest first. Backed by the `idx_cell_refs` GIN index.
 *
 * Used by the fact-serving route's authz walk: a fact is viewable iff
 * **at least one** referencing active cell admits the caller via
 * `can_view_cell`. Unreferenced facts (no row returned here) are
 * unreachable through the public surface — orphan-fact GC handles them.
 *
 * `include_grant_count` defaults to true so the row hydrates uniformly
 * with the rest of the cell query surface. The fact-serving route is
 * the one hot path where the count is wasted work — pass `false`
 * there to skip the per-row correlated subquery; the field falls back
 * to a constant 0 so `CellRow` stays type-stable.
 *
 * @param deps - query deps
 * @param hash - fact hash to search for
 * @param options - pagination + grant-count toggle
 * @returns matching active rows
 */
export declare const query_cell_list_by_ref: (deps: QueryDeps, hash: FactHash, options?: Pick<CellListOptions, "limit" | "offset"> & {
    include_grant_count?: boolean;
}) => Promise<Array<CellRow>>;
//# sourceMappingURL=cell_queries.d.ts.map