import type { AuthUser } from './auth-service.js';
export type CommentAuthorRole = 'user' | 'agent';
export interface Comment {
    commentId: string;
    repo: string;
    entryType: string;
    entryId: string;
    threadId: string;
    parentId: string | null;
    authorUserId: number;
    authorDisplayName: string;
    authorRole: CommentAuthorRole;
    body: string;
    /** Base64 of the serialized Yjs RelativePosition pair, or null for entry-level threads. */
    anchorRelpos: string | null;
    anchorQuote: string | null;
    mentions: AuthUser[];
    createdAt: string;
    updatedAt: string | null;
    resolvedAt: string | null;
    resolvedByUserId: number | null;
    edited: 0 | 1;
    editHistory: Array<{
        at: string;
        body: string;
    }>;
    deletedAt: string | null;
}
export interface CreateCommentInput {
    repo: string;
    entryType: string;
    entryId: string;
    authorUserId: number;
    authorDisplayName: string;
    authorRole?: CommentAuthorRole;
    body: string;
    anchorRelpos?: string | null;
    anchorQuote?: string | null;
    mentions?: AuthUser[];
    /** If provided, this comment is a reply within an existing thread. */
    parentId?: string | null;
    threadId?: string | null;
}
export interface ListCommentsOpts {
    repo?: string;
    entryType?: string;
    entryId?: string;
    resolved?: boolean;
    includeDeleted?: boolean;
    limit?: number;
}
/** Largest comment body the service will accept. Above this the route
 *  returns 413 — Fastify bodyLimit is the outer 10MB wall, this is the
 *  per-comment policy ceiling. */
export declare const MAX_BODY_LENGTH: number;
export declare const MAX_ANCHOR_RELPOS_LENGTH: number;
/** Sentinel author_user_id used for agent-authored comments. The auth.db
 *  users table is AUTOINCREMENT (starts at 1), so id=0 can never collide
 *  with a real user. */
export declare const AGENT_AUTHOR_USER_ID = 0;
/** Thrown by create() when body or anchor_relpos exceeds the ceiling. */
export declare class CommentTooLargeError extends Error {
    constructor(message: string);
}
/** Thrown by create() when a cell (JSON) anchor is malformed, or when a JSON
 *  cell anchor is attached to a non-dataset entry (markdown comments only ever
 *  carry a base64 text relpos or a null entry-level anchor). The route maps it
 *  to HTTP 400. */
export declare class InvalidCellAnchorError extends Error {
    constructor(message: string);
}
export declare class CommentService {
    private db;
    constructor(workspacePath: string);
    /**
     * Idempotent: each migration step is guarded by the recorded
     * `schema_version` in `meta`. Add a new branch when bumping
     * `SCHEMA_VERSION`; never edit existing branches.
     */
    private runMigrations;
    create(input: CreateCommentInput): Comment;
    getById(commentId: string): Comment | null;
    /** Batch lookup used by the notifications route to enrich each notification
     *  with its source comment's entry coords. Returns a map keyed by commentId
     *  so callers can do a single pass. Missing ids are absent from the map. */
    getByIds(commentIds: string[]): Map<string, Comment>;
    list(opts?: ListCommentsOpts): Comment[];
    edit(commentId: string, body: string, editorUserId: number): Comment | null;
    resolve(commentId: string, resolverUserId: number): Comment | null;
    reopen(commentId: string): Comment | null;
    /** Soft-delete: keeps thread continuity, blanks the visible body but
     *  preserves the original in edit_history so a future audit/restore
     *  path has something to recover. */
    softDelete(commentId: string, requesterUserId: number): Comment | null;
    close(): void;
    private toComment;
}
