import { GroupInfo } from './table.js';
export interface NotificationRecord {
    api?: string;
    app_id?: string;
    enabled?: boolean;
    id?: string;
    model?: string;
    name?: string;
    v2?: boolean;
}
export type ParsedNotificationKey = {
    id: string;
    kind: 'hex';
} | {
    id: string;
    kind: 'system_id';
} | {
    kind: 'app_full';
    model: string;
    name: string;
    slug: string;
} | {
    kind: 'app_short';
    name: string;
    slug: string;
} | {
    kind: 'bare';
    name: string;
} | {
    input: string;
    kind: 'invalid';
};
/**
 * Translate a stored model path into its display form.
 *
 *   apps/<own-hex>/<x>      → <x>          (own-app prefix is implicit in the slug)
 *   apps/<other-hex>/<x>    → apps/<other-slug>/<x>
 *   apps/<unknown-hex>/<x>  → unchanged    (preserve a paste-back form)
 *   <built-in-model>        → unchanged
 *
 * Stripping `apps/<own>/` keeps the common case readable; the rare collision
 * with a same-named built-in (e.g. an app model literally named
 * `subscriptions`) is recovered by `expandModelCandidates` at lookup time.
 */
export declare function normalizeModelForDisplay(model: string, ownAppId: string | undefined, appSlugById: Record<string, string>): string;
/**
 * Produce the column-1 paste-back key for a notification.
 *
 *   System (no app_id):   record.id verbatim — already in `com.<model>.<name>` form.
 *   App with model+name:  `app.<slug>.<model-display>.<name>`
 *   Anything else:        record.id (degenerate, no meaningful slug)
 *
 * Notification identity is `(api, model, name, app_id?)`; collapsing on
 * `(app_id, name)` alone caused two `notify_me.back-in-stock` rows
 * (different `model`) to render identically.
 */
export declare function formatNotificationKey(record: NotificationRecord, appSlugById: Record<string, string>): string;
/**
 * Group a notification record. System rows (no app_id) are not store data —
 * they belong to the platform and render under a `system` divider at the top.
 */
export declare function groupNotificationRecord(record: NotificationRecord, appSlugById: Record<string, string>): GroupInfo;
/**
 * Classify a notification identifier. The base `classifyIdentifier` collapses
 * every dot-segment after `app.<part>.` into `name`, which loses the model.
 * Notifications need a model-aware split, so they bypass the base classifier.
 *
 * Recognised shapes:
 *   - 24-char hex                    → hex (delegate to base GET-by-id)
 *   - com.<rest>                     → system_id (canonical platform id)
 *   - app.<slug>.<model>.<name>      → app_full (triplet lookup)
 *   - app.<slug>.<name>              → app_short (3-part legacy; ambiguous)
 *   - bare <name>                    → bare (requires --app= scope; ambiguous)
 *   - anything else                  → invalid
 *
 * Multi-segment names (e.g. `unpaid.v2`) only appear in system notifications,
 * which arrive via the `com.*` form and never go through `app.*` parsing.
 */
export declare function parseNotificationKey(input: string): ParsedNotificationKey;
/**
 * Render the disambiguation error body when a `(app_id, name)` query returns
 * multiple notifications. Caller fetches with `limit = displayLimit + 1` so
 * we can detect overflow without paging through the whole result set; if
 * `results.length > displayLimit`, the surplus rows are dropped and a hint
 * line is appended directing the user to the full key form.
 */
export declare function formatDisambiguationError(results: NotificationRecord[], appSlugById: Record<string, string>, identifier: string, displayLimit?: number): string;
/**
 * Build the `template` value used on send records at `/notifications` from a
 * manifest record's fields. Three shapes:
 *
 *   System (no app_id):                 <model>.<name>
 *   App on a standard model:            app_<app_id>.<model>.<name>
 *   App on a custom (apps/<hex>/x) model: app_<app_id>.apps_<hex>_<x>.<name>
 *
 * The custom-model branch transforms slashes to underscores in the model
 * segment.
 *
 * V2 system notifications have `name` literally ending in `.v2` (mirroring the
 * `id` and the `v2: true` flag), but the send-record `template` strips that
 * suffix. App notifications carry `v2: true` without the suffix in `name`, so
 * they pass through unchanged.
 *
 * Returns undefined when `model` or `name` is missing.
 */
export declare function buildNotificationTemplate(record: NotificationRecord): string | undefined;
/**
 * Expand a display-form model into the candidate stored values to try at
 * lookup time. A bare display token can mean either:
 *   - the literal built-in model (e.g. `subscriptions`)
 *   - an own-app model whose `apps/<own>/` prefix was stripped for display
 * so we try both. `apps/<slug>/x` is resolved to `apps/<hex>/x`; an already-hex
 * `apps/<hex>/x` passes through unchanged.
 */
export declare function expandModelCandidates(modelDisplay: string, appId: string, resolveAppId: (slug: string) => Promise<string>): Promise<string[]>;
