/**
 * Suggestions feature — public type surface for v1.
 *
 * Source contract: docs/suggestions-api-contract.md
 * Source flows:    docs/suggestions-implementation-flows.md
 *
 * Types are exported from the SDK's public barrel. Customer code interacts
 * with these via Snippyly.getSuggestionElement(); the SDK constructs all
 * Suggestion objects internally and customers do not build them directly.
 */
import { User } from './user.data.model';
/**
 * Lifecycle state machine for a Suggestion.
 *
 * - pending:      Just created, awaiting owner action.
 * - approved:     Owner clicked Approve. Customer apply handler ran successfully.
 * - rejected:     Owner clicked Reject (with optional reason).
 * - stale:        Target was unresolvable at approve time. Owner can dismiss.
 * - apply_failed: Customer apply handler threw during a suggestionApproved event.
 *                 Status set by the SDK after the throw was caught.
 */
export type SuggestionStatus = 'pending' | 'accepted' | 'rejected' | 'stale' | 'apply_failed';
/**
 * Global per-user-per-session suggestion mode. Not persisted; reload returns to 'editing'.
 */
export type SuggestionMode = 'editing' | 'suggesting';
/**
 * Discriminator for the substrate that produced a suggestion. v1: always 'custom'.
 * Wrapper libraries (e.g. tiptap-velt-comments) widen this union when they integrate.
 *
 * Customers should treat unknown values as opaque; the field exists so customer code
 * can differentiate substrate when domain-specific rendering matters.
 */
export type SuggestionTargetType = 'custom';
/**
 * Single-arg config passed to `SuggestionElement.registerTarget(config)`.
 * Carrying both `targetId` and `getter` in one object so the API can grow
 * (e.g., metadata, target-specific options) without breaking customers.
 */
export interface RegisterTargetConfig<T = unknown> {
    targetId: string;
    getter: TargetGetter<T>;
}
/**
 * Function the customer registers via SuggestionElement.registerTarget(config).
 * Required for any non-primitive (wrapper) target.
 *
 * The SDK calls this getter twice in a typical edit cycle:
 *   1. On focus of the tagged element — to snapshot the pre-edit value.
 *   2. On commit (focusout / change) — to read the current value for the diff.
 *
 * IMPORTANT — edit-time state, not persisted state: the getter must reflect
 * what the user is currently editing, not what's persisted in the customer's
 * app store. If the customer's state is only updated on commit/approve (as
 * is typical when suggesting mode is on), reading from that state would
 * return the snapshot value at commit time, the diff check would short-
 * circuit, and no suggestion would ever fire.
 *
 * Recommended: read from the DOM (controlled or uncontrolled inputs) so the
 * getter always returns what's visible to the user.
 *
 *   el.registerTarget('row.123', () => ({
 *     qty:   parseInt(qtyInput.value, 10),
 *     price: parseInt(priceInput.value, 10),
 *   }));
 *
 * For controlled inputs (state updates on every keystroke), reading from
 * the customer state is fine: `() => myState.row123`. The contract is
 * "edit-time state" — whichever source of truth has it.
 */
export type TargetGetter<T = unknown> = () => T;
/**
 * Returned from SuggestionElement.on(...). Calling it removes the handler.
 */
export type Unsubscribe = () => void;
/**
 * Details about a target edit, passed to the customer's resolver handler and
 * to subscribers of `targetEditStart` / `targetEditCommit` events.
 *
 * `element` is the DOM element that produced the edit. It's the tagged
 * descendant — or, for wrappers that nest interactive controls inside a
 * tagged container, the inner element that actually fired the event. In
 * either case the SDK walks up to the nearest ancestor carrying
 * `data-velt-suggestion-target` to resolve `targetId`.
 *
 * `element` is provided for read access only — customers should not retain
 * the reference past the synchronous handler call.
 */
export interface TargetEditDetails<T = unknown> {
    targetId: string;
    oldValue: T;
    newValue: T;
    element: Element | null;
}
/**
 * Return type of `onTargetEditCommit`. Returning a value auto-commits the
 * suggestion using the (optional) overrides; returning null skips the
 * auto-commit so a subscriber to `targetEditCommit` can drive it explicitly.
 */
export interface TargetEditCommitResult {
    /** Override the SDK's default `${targetId}: ${old} → ${new}` summary. */
    summary?: string;
    /** Customer metadata persisted on the resulting Suggestion. */
    metadata?: Record<string, unknown>;
}
/**
 * Return type of `onTargetEditStart`. Reserved for future fields; v1 has
 * no behavior-bearing return values, so customers may omit a return.
 *
 * Roadmap: future versions may accept `{ oldValue }` here so customers can
 * override the SDK's auto-snapshot with a domain-canonical value.
 */
export interface TargetEditStartResult {
}
export type TargetEditStartHandler<T = unknown> = (details: TargetEditDetails<T>) => TargetEditStartResult | void | null;
export type TargetEditCommitHandler<T = unknown> = (details: TargetEditDetails<T>) => TargetEditCommitResult | null;
/**
 * Config passed to `SuggestionElement.enableSuggestionMode(config?)`.
 * Both callbacks are optional. If `onTargetEditCommit` is omitted, customers
 * can still drive auto-commit by subscribing to the `targetEditCommit` event
 * and calling the pre-bound `commitSuggestion` builder on the payload.
 */
export interface EnableSuggestionModeConfig {
    /**
     * Invoked on focus of a tagged element, after the SDK captures the
     * snapshot. v1: informational — return value is reserved for future
     * use (e.g. `{ oldValue }` to override the SDK's auto-snapshot).
     */
    onTargetEditStart?: TargetEditStartHandler;
    /**
     * Invoked once per detected commit (on `change` for atomic inputs, on
     * `focusout` for text-like inputs). Returning a `TargetEditCommitResult`
     * auto-commits with the supplied summary/metadata; returning null defers
     * to event subscribers.
     */
    onTargetEditCommit?: TargetEditCommitHandler;
}
/**
 * SDK-managed suggestion data persisted on a CommentAnnotation.
 * Present iff annotation.type === 'suggestion'.
 *
 * Customer code must not write to this directly — use the SuggestionElement API.
 */
export interface SuggestionData {
    /** Lifecycle state machine. */
    status: SuggestionStatus;
    /** Stable, customer-owned target identifier. */
    targetId: string;
    /** v1: always 'custom'. Wrappers widen later. */
    targetType: SuggestionTargetType;
    /** Snapshot taken at startSuggestion / focus time. Frozen via structuredClone. */
    oldValue: any;
    /** Value submitted via commitSuggestion. */
    newValue: any;
    /** Optional human-readable description for logs / notifications. */
    summary: string | null;
    /**
     * True if the live value at approve time differed from oldValue.
     * v1 records flag only; v1.1 will surface a confirmation prompt.
     */
    driftDetected: boolean;
    /** Populated only when status === 'rejected'. */
    rejectReason: string | null;
    /**
     * Full User snapshot of the resolver, populated when status moves to
     * approved | rejected | stale | apply_failed. Snapshot rather than userId
     * so customer code can render name/email/photoUrl without an extra lookup,
     * and so historical suggestions retain their resolver record even if the
     * user later updates their profile.
     */
    resolvedBy: User | null;
    resolvedAt: number | null;
}
/**
 * Fields shared across every Suggestion regardless of status.
 * Internal — used to build the per-status discriminated types below.
 */
interface SuggestionBase<T = unknown> {
    /** Annotation document id (same as the underlying CommentAnnotation.annotationId). */
    annotationId: string;
    /** Stable, customer-owned target identifier. */
    targetId: string;
    /** v1: always 'custom'. */
    targetType: SuggestionTargetType;
    /** Snapshot taken at startSuggestion / focus time. */
    oldValue: T;
    /** Value submitted via commitSuggestion. */
    newValue: T;
    /** Optional human-readable description. */
    summary: string | null;
    /** Customer-defined metadata supplied via CommitSuggestionConfig.metadata. */
    metadata: Record<string, any>;
    /** True iff the live value at approve time differed from oldValue. */
    driftDetected: boolean;
    /**
     * User of the suggestion's creator (sourced from annotation.from).
     */
    createdBy?: User;
    createdAt: number;
}
/** A suggestion in the 'pending' state — newly created, no owner action yet. */
export interface PendingSuggestion<T = unknown> extends SuggestionBase<T> {
    status: 'pending';
    rejectReason: null;
    resolvedBy: null;
    resolvedAt: null;
}
/** A suggestion that has been approved (apply handler success or pending invocation). */
export interface ApprovedSuggestion<T = unknown> extends SuggestionBase<T> {
    status: 'accepted' | 'apply_failed';
    rejectReason: null;
    resolvedBy: User;
    resolvedAt: number;
}
/**
 * A suggestion that has been rejected. `rejectReason` may be null when the
 * rejecter dismissed without supplying a reason — matches the persisted
 * `SuggestionData.rejectReason: string | null` shape.
 */
export interface RejectedSuggestion<T = unknown> extends SuggestionBase<T> {
    status: 'rejected';
    rejectReason: string | null;
    resolvedBy: User;
    resolvedAt: number;
}
/** A suggestion whose target was unresolvable at approve time. */
export interface StaleSuggestion<T = unknown> extends SuggestionBase<T> {
    status: 'stale';
    rejectReason: null;
    resolvedBy: User | null;
    resolvedAt: number | null;
}
/**
 * Public Suggestion — discriminated union keyed by `status`. TypeScript narrows
 * field types per status (e.g. `resolvedBy: User` is non-null on approved/rejected,
 * `rejectReason: string | null` on rejected since one-click reject without a reason
 * is supported).
 */
export type Suggestion<T = unknown> = PendingSuggestion<T> | ApprovedSuggestion<T> | RejectedSuggestion<T> | StaleSuggestion<T>;
export interface CommitSuggestionConfig<T = unknown> {
    /**
     * Must be registered (via data-velt-target attribute or registerTarget call)
     * before commit, otherwise the suggestion is rejected with a dev-mode warning.
     */
    targetId: string;
    /** Any JSON-serializable value. Customer's apply handler interprets it. */
    newValue: T;
    /** Optional human-readable string for logs and notifications. */
    summary?: string;
    /** Optional customer-defined metadata. Stored on Suggestion.metadata. */
    metadata?: Record<string, unknown>;
}
/**
 * Pre-bound builder attached to `targetEditCommit` payloads. Calling it
 * commits the suggestion with the SDK's default summary/metadata, optionally
 * overridden by `result`. If the customer's `onTargetEditCommit` handler
 * already returned a non-null result for this edit, this builder is a no-op
 * (resolves immediately) so subscribers can't double-commit.
 */
export type TargetEditCommitBuilder = (result?: TargetEditCommitResult) => Promise<{
    id: string;
} | null>;
export interface SuggestionGetSuggestionsFilter {
    targetId?: string;
    status?: SuggestionStatus | SuggestionStatus[];
}
export {};
