import type { AttachmentStagingDescription, AttachmentStagingStore, ListStagedAttachmentsInput, ResolveAttachmentInput, StageAttachmentInput, StagedAttachment } from '../spi/attachment-staging.js';
import type { Actor, MessageAttachment } from '../types.js';
/** A media row — `@adonis-agora/media`'s `MediaRecord`. */
export interface MediaRecordLike {
    id: string;
    ownerType: string;
    ownerId: string;
    collection: string;
    name: string;
    fileName: string;
    mimeType: string;
    size: number;
    disk: string;
    path: string;
    order: number;
    customProperties: Record<string, unknown>;
    conversions: Record<string, unknown>;
    createdAt: Date;
    updatedAt: Date;
}
/** `@adonis-agora/media`'s `MediaStore`, the part used here. */
export interface MediaStoreLike {
    save(record: MediaRecordLike): Promise<MediaRecordLike>;
    find(id: string): Promise<MediaRecordLike | null>;
    delete(id: string): Promise<void>;
    nextOrder(ownerType: string, ownerId: string, collection: string): Promise<number>;
    /** Every media row of one owner (in one collection) — what the staged-attachment inventory reads. */
    listByOwner?(ownerType: string, ownerId: string, collection?: string): Promise<MediaRecordLike[]>;
}
/** A Drive disk as `@adonis-agora/media` hands it out, the part used here. */
export interface MediaDiskLike {
    put(key: string, contents: Uint8Array, options?: {
        contentType?: string;
    }): Promise<void>;
    getBytes(key: string): Promise<Uint8Array>;
    getUrl(key: string): Promise<string>;
    getSignedUrl(key: string, options?: {
        expiresIn?: string | number;
        contentType?: string;
    }): Promise<string>;
    delete(key: string): Promise<void>;
    /** Needed for resumable uploads only — whether the assembled object has landed. */
    exists?(key: string): Promise<boolean>;
    /** Needed for resumable uploads only — the assembled object's size. */
    getMetaData?(key: string): Promise<{
        contentLength: number;
    }>;
}
/**
 * `@adonis-agora/media`'s `ResumableUploadManager` (`media.resumable`), the part used here: open a
 * tus session the bytes then stream to over the media library's own tus routes, or abort one.
 */
export interface MediaResumableLike {
    createUpload(input: {
        disk: string;
        key: string;
        size?: number;
        contentType?: string;
        metadata?: Record<string, string>;
    }): Promise<{
        id: string;
    }>;
    abort(id: string): Promise<void>;
}
/** What `beginUpload` takes: the file as the client describes it, before any byte moves. */
export interface BeginMediaUploadInput {
    actor: Actor;
    filename: string;
    contentType: string;
    /** Exact byte length — tus needs it up front, and completion is checked against it. */
    size: number;
}
/** A tus session opened for one attachment. Stream the bytes to `location`, then complete. */
export interface BeginMediaUploadResult {
    /** The attachment's id — what the chat turn sends as `{ mediaId }`. */
    mediaId: string;
    /** The tus upload id. */
    uploadId: string;
    /** The tus resource to `PATCH` the bytes to (relative to the app's origin). */
    location: string;
}
/** A refusal of an upload request, carrying the HTTP status the route answers with. */
export declare class MediaUploadRefusedError extends Error {
    readonly status: 400 | 409 | 413 | 415 | 422 | 501;
    constructor(status: 400 | 409 | 413 | 415 | 422 | 501, message: string);
}
/** The agent store, the part used here — which media a live message of this actor carries. */
export interface ReferencedMediaLike {
    referencedMediaIds?(actorRef: string, mediaIds: readonly string[]): Promise<string[]>;
}
/** What {@link MediaAttachmentStaging} is built from. */
export interface MediaAttachmentStagingDeps {
    storage: MediaStorageLike;
    store: MediaStoreLike;
    /** Lets media referenced from the actor's own threads resolve (see `canAccess`). */
    agentStore?: ReferencedMediaLike;
    /**
     * `media.resumable` — resumable (tus) uploads. Absent or `null` (`config/media.ts` has no
     * `uploads.resumable`) → uploads are multipart only.
     */
    uploads?: MediaResumableLike | null;
}
/** `@adonis-agora/media`'s `StorageManager`, the part used here. */
export interface MediaStorageLike {
    readonly defaultDisk: string;
    disk(name?: string): MediaDiskLike;
}
/** Options for `attachmentStores.media({ … })`. All optional. */
export interface MediaAttachmentsOptions {
    /** Media collection the uploads go in. Default `agent-attachments`. */
    collection?: string;
    /** Owner type the rows carry (the owner id is the actor id). Default `agent-actor`. */
    ownerType?: string;
    /** Drive disk. Default the media library's default disk. */
    disk?: string;
    /** Per-file size cap. Default 20 MiB. */
    maxBytes?: number;
    /** Content types accepted. Default images (png/jpeg/gif/webp), PDF, plain text, CSV. */
    allowedContentTypes?: readonly string[];
    /**
     * `private` (default): urls are signed and short-lived, re-minted on every read. `public`: the
     * disk's plain url.
     */
    visibility?: 'private' | 'public';
    /** Lifetime of a signed url, in seconds. Default 7 days. */
    urlExpiresInSeconds?: number;
    /** Mint the url yourself (a CDN, a proxy route). Wins over `visibility`. */
    resolveUrl?: (record: MediaRecordLike, context: {
        disk: MediaDiskLike;
    }) => string | Promise<string>;
    /**
     * Who may attach a file. By default (`allowed`): the actor that uploaded it, or one a message in
     * one of the actor's OWN threads already carries (a fork, a regenerate) — the latter needs an
     * agent store with `referencedMediaIds`, which both shipped stores have. Widen or narrow it here —
     * e.g. let a support agent attach a customer's upload.
     */
    canAccess?: (input: {
        record: MediaRecordLike;
        actor: Actor;
        allowed: boolean;
    }) => boolean | Promise<boolean>;
    /**
     * Where `@adonis-agora/media` mounts its tus routes (`uploads.resumable.routes.prefix` in
     * `config/media.ts`). Default `/media/uploads/tus`. The `location` a resumable upload answers is
     * `<tusBasePath>/<uploadId>`.
     */
    tusBasePath?: string;
    /** Clock and id seams, for tests. */
    clock?: () => Date;
    idGenerator?: () => string;
}
export declare const DEFAULT_AGENT_MEDIA_COLLECTION = "agent-attachments";
export declare const DEFAULT_AGENT_MEDIA_OWNER_TYPE = "agent-actor";
export declare const DEFAULT_AGENT_MEDIA_TUS_BASE_PATH = "/media/uploads/tus";
/**
 * Chat attachments stored as media rows in their own collection, one owner per actor, on any Drive
 * disk — S3, GCS, R2, the local filesystem. Uploads land at
 * `<ownerType>/<actor>/<collection>/<id>/<file>`; a send names them by id and {@link resolve} mints
 * the url the model fetches: the caller's own `resolveUrl`, a public url, a signed url, or — on a
 * disk that cannot sign — the bytes inline as a `data:` url (logged once: fine for dev, not for
 * production).
 */
export declare class MediaAttachmentStaging implements AttachmentStagingStore {
    private readonly deps;
    private readonly options;
    private readonly collection;
    private readonly ownerType;
    private readonly maxBytes;
    private readonly allowed;
    private readonly now;
    private readonly newId;
    private warnedInline;
    constructor(deps: MediaAttachmentStagingDeps, options?: MediaAttachmentsOptions);
    describe(): AttachmentStagingDescription;
    /**
     * Open a tus session for one file, owned by `actor`. Everything is validated here, before any
     * byte moves. The record exists from now on, `pending` until {@link completeUpload} sees the bytes.
     */
    beginUpload(input: BeginMediaUploadInput): Promise<BeginMediaUploadResult>;
    /**
     * Confirm the bytes of a {@link beginUpload} landed, and whole. `null` for an unknown id or one
     * that is not `actor`'s; `409` while the bytes are still missing; `422` (and the record dropped)
     * when what arrived is not the size that was declared. Idempotent on a ready record.
     */
    completeUpload(input: {
        actor: Actor;
        mediaId: string;
    }): Promise<MessageAttachment | null>;
    /** The actor drops one of their own attachments — aborting its upload if still in flight. */
    discard(input: {
        actor: Actor;
        mediaId: string;
    }): Promise<boolean>;
    private get diskName();
    stage(input: StageAttachmentInput): Promise<MessageAttachment>;
    /**
     * The attachment for `mediaId`, or `null` when it is unknown or this actor may not use it. The
     * actor may use media they own, and media a message in one of THEIR threads already carries (a
     * fork, a regenerate) — never anything else, unless `canAccess` says so.
     */
    resolve(input: ResolveAttachmentInput): Promise<MessageAttachment | null>;
    /**
     * The actor's staged attachments, newest first: their own media rows in this store's collection —
     * sent or not, and a resumable upload still in flight too (it is theirs, and it is staged). Metadata
     * only; a url is only ever minted by {@link resolve}.
     */
    list(input: ListStagedAttachmentsInput): Promise<StagedAttachment[]>;
    private canUse;
    private defaultAccess;
    /** Delete an upload and its row. */
    remove(mediaId: string): Promise<void>;
    private validate;
    private pathFor;
    private saveRecord;
    private ownRecord;
    /**
     * A ready record as-is; a pending one promoted to ready once its bytes are on the disk at exactly
     * the declared size. `'missing'` while they are not; `'mismatch'` when they arrived at another size.
     */
    private ensureReady;
    private isOurs;
    private toAttachment;
}
//# sourceMappingURL=media-attachment-staging.d.ts.map