import { BaseEngine, BaseEntityEvent, IMetadataProvider, UserInfo } from "@memberjunction/core";
import { Observable } from "rxjs";
import { MJConversationEntity, MJConversationDetailEntity, MJConversationDetailEntityType, MJAIAgentRunEntity, MJAIAgentRunEntityType, MJConversationDetailRatingEntityType, MJConversationDetailArtifactEntityType, MJProjectEntity } from "../generated/entity_subclasses.js";
/**
 * Display info about the user who shared a conversation with the current user,
 * plus the `PermissionLevel` the grantee was granted. Exposed via
 * {@link ConversationEngine.GetSharedByInfo} for UI surfaces that render
 * "Shared by {email}" badges and gate actions (e.g., View-only recipients
 * can't send messages).
 */
/**
 * Optional scoping inputs to {@link ConversationEngine.CreateConversation}.
 * Forwards to the new ApplicationScope / ApplicationID / DefaultAgentID
 * columns on Conversation. All fields optional; omit for the default
 * 'Global' main-chat behavior. Embedded chat surfaces (e.g. the Form
 * Builder cockpit) should pass `applicationScope: 'Application'` plus an
 * `applicationId` so the conversation doesn't pollute the main chat list.
 */
export interface CreateConversationOptions {
    /**
     * Where this conversation surfaces in the UI:
     *  - 'Global'      → main Chat app (no app binding). Default.
     *  - 'Application' → embedded surface only; hidden from main chat.
     *  - 'Both'        → visible in both surfaces by default.
     * DB CHECK constraint requires applicationId to match (Application/Both
     * ⇒ applicationId NOT NULL; Global ⇒ applicationId NULL).
     */
    applicationScope?: 'Global' | 'Application' | 'Both';
    /** The owning Application's ID. Required when scope is Application or Both. */
    applicationId?: string | null;
    /** Optional per-conversation pinned default agent (e.g. Research Agent). */
    defaultAgentId?: string | null;
    /**
     * "What is this conversation about?" pointer — the Entity whose record
     * this conversation references. Paired with {@link linkedRecordId} via
     * the DB CHECK constraint `CK_Conversation_LinkBinding`: both NULL or
     * both populated. Form Builder cockpit passes the MJ: Components
     * entity ID + the active form's ComponentID so the cockpit can later
     * filter "prior conversations about THIS form."
     */
    linkedEntityId?: string | null;
    /**
     * Primary key of the linked record, serialized as a string. Used with
     * {@link linkedEntityId}. NVARCHAR(500) in the DB — handles any PK shape
     * (UUID, int, composite).
     */
    linkedRecordId?: string | null;
}
export interface SharedByInfo {
    /** Grantor user ID. Null when the share predates the `SharedByUserID` column. */
    UserID: string | null;
    Name: string | null;
    Email: string | null;
    /** Level the current user was granted on this conversation. */
    Level: 'View' | 'Edit' | 'Owner';
}
/**
 * Agent Run data returned as JSON from GetConversationComplete query.
 */
export type AgentRunJSON = MJAIAgentRunEntityType & {
    Agent: string | null;
};
/**
 * Artifact data returned as JSON from GetConversationComplete query.
 */
export type ArtifactJSON = MJConversationDetailArtifactEntityType & {
    ArtifactVersionID: string;
    VersionNumber: number;
    VersionName: string | null;
    VersionDescription: string | null;
    VersionCreatedAt: Date;
    ArtifactID: string;
    ArtifactName: string;
    ArtifactType: string;
    ArtifactDescription: string | null;
    Visibility: string;
};
/**
 * Rating data returned as JSON from GetConversationComplete query.
 */
export type RatingJSON = MJConversationDetailRatingEntityType & {
    UserName: string;
};
/**
 * Raw query result row from GetConversationComplete (before JSON parsing).
 */
export type ConversationDetailComplete = MJConversationDetailEntityType & {
    AgentRunsJSON: string | null;
    ArtifactsJSON: string | null;
    RatingsJSON: string | null;
    UserImageURL: string | null;
    UserImageIconClass: string | null;
};
/**
 * Parsed conversation detail with typed related data.
 */
export interface ConversationDetailParsed extends MJConversationDetailEntityType {
    agentRuns: AgentRunJSON[];
    artifacts: ArtifactJSON[];
    ratings: RatingJSON[];
}
/**
 * Helper: parse a raw ConversationDetailComplete row into typed arrays.
 */
export declare function parseConversationDetailComplete(queryResult: ConversationDetailComplete): ConversationDetailParsed;
/** User avatar info extracted from the query */
export interface UserAvatarInfo {
    ImageURL: string | null;
    IconClass: string | null;
}
/**
 * Cached data for a single conversation's details (messages) and peripheral data.
 * Populated by the efficient GetConversationComplete query in one round-trip.
 */
export interface ConversationDetailCache {
    /** The conversation detail (message) entities */
    Details: MJConversationDetailEntity[];
    /** Raw query result rows (used for peripheral data parsing) */
    RawData: ConversationDetailComplete[];
    /** Agent runs keyed by conversation detail ID */
    AgentRunsByDetailId: Map<string, MJAIAgentRunEntity>;
    /** User avatars keyed by UserID */
    UserAvatars: Map<string, UserAvatarInfo>;
    /** Ratings keyed by conversation detail ID */
    RatingsByDetailId: Map<string, RatingJSON[]>;
    /** Parsed artifacts keyed by conversation detail ID */
    ArtifactsByDetailId: Map<string, ArtifactJSON[]>;
    /** Timestamp of when this cache entry was populated */
    LoadedAt: Date;
    /**
     * When true, peripheral data (artifacts/ratings) has changed externally and
     * needs to be re-fetched via a full query reload. Set by entity event handlers
     * for junction entities whose joined fields can't be reconstructed from events alone.
     */
    PeripheralDataStale: boolean;
}
/**
 * ConversationEngine provides centralized, reactive caching for conversations,
 * conversation details (messages), and peripheral data (agent runs, artifacts).
 *
 * This engine is the single source of truth for conversation data across all UI
 * consumers (chat area, sidebar, overlay, etc.). It replaces per-component caching
 * that previously lived in conversation-chat-area component,
 * and other scattered locations.
 *
 * Usage:
 * ```typescript
 * // Initialize (call once at app startup after metadata is loaded)
 * await ConversationEngine.Instance.Config(false, contextUser);
 *
 * // Load conversations for the current user
 * await ConversationEngine.Instance.LoadConversations('env-id', contextUser);
 *
 * // Subscribe to conversation list changes
 * ConversationEngine.Instance.Conversations$.subscribe(conversations => {
 *     // React to changes
 * });
 *
 * // Load details for a specific conversation
 * const details = await ConversationEngine.Instance.LoadConversationDetails('conv-id', contextUser);
 *
 * // Get cached details (instant, no DB round-trip)
 * const cached = ConversationEngine.Instance.GetCachedDetails('conv-id');
 * ```
 */
export declare class ConversationEngine extends BaseEngine<ConversationEngine> {
    /**
     * Returns the global instance of the class. This is a singleton class, so there is only
     * one instance of it in the application. Do not directly create new instances of it,
     * always use this method to get the instance.
     */
    static get Instance(): ConversationEngine;
    private _conversations$;
    /**
     * Observable stream of the current user's conversation list.
     * Emits whenever conversations are loaded, created, deleted, archived, or pinned.
     */
    get Conversations$(): Observable<MJConversationEntity[]>;
    /**
     * Current snapshot of conversations (non-reactive).
     */
    get Conversations(): MJConversationEntity[];
    private _projects$;
    /**
     * Observable stream of the projects (conversation folders) for the current
     * environment. Emits whenever projects are loaded or a project is created,
     * renamed, or deleted (kept in sync via the entity event handler).
     */
    get Projects$(): Observable<MJProjectEntity[]>;
    /**
     * Current snapshot of projects/folders (non-reactive).
     */
    get Projects(): MJProjectEntity[];
    /** Detail cache keyed by normalized conversation ID */
    private _detailCache;
    /** Track the environment ID used for the last load */
    private _lastEnvironmentId;
    /** Track the environment ID used for the last projects (folders) load */
    private _lastProjectsEnvironmentId;
    /**
     * For conversations the current user *received* via sharing, this map goes
     * from `conversationId` to the grantor's display info. Populated by
     * {@link LoadConversations} using the `SharedByUserID` column on the
     * `MJ: Resource Permissions` row. Used by the chat UI to render
     * "Shared by {email}" next to the title and a share icon in the sidebar.
     */
    private _sharedByByConversationId;
    /**
     * Look up grantor info for a conversation the current user was shared into.
     * Returns `null` for conversations the user owns (not shared with them).
     */
    GetSharedByInfo(conversationId: string): SharedByInfo | null;
    /**
     * Guard flag: set true while the engine itself is performing a mutation.
     * Prevents the entity event handler from re-processing our own saves/deletes,
     * which would cause redundant cache updates or infinite loops.
     */
    private _selfMutating;
    /**
     * Configures the engine. Unlike other engines that bulk-load entity tables via BaseEngine.Load(),
     * ConversationEngine manages its own caching because conversations are user-scoped and filtered
     * by environment, which doesn't fit the standard "load all rows" pattern.
     *
     * Call this once at startup to initialize the engine. Conversation data is loaded
     * separately via LoadConversations().
     */
    Config(forceRefresh?: boolean, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<void>;
    /**
     * Loads conversations from the database for the given user and environment.
     * Results are cached and emitted via Conversations$.
     *
     * @param environmentId - The environment to filter conversations by
     * @param contextUser - The current user context
     * @param forceRefresh - If true, reloads even if data is already cached
     */
    LoadConversations(environmentId: string, contextUser: UserInfo, forceRefresh?: boolean, options?: {
        includeApplicationScoped?: boolean;
    }): Promise<void>;
    /**
     * Populates {@link _sharedByByConversationId}. Resolves grantor emails via
     * one `MJ: Users` query keyed on the unique set of `SharedByUserID`s.
     * Rows without a `SharedByUserID` (legacy shares predating the column)
     * are skipped — the UI degrades to no badge for those.
     */
    private rebuildSharedByMap;
    /**
     * Loads the projects (conversation folders) for an environment and emits via Projects$.
     * Projects are environment-scoped (not user-scoped) and small, so the full active set
     * is cached. Skips reloading when already loaded for the same environment unless forced.
     *
     * @param environmentId - The environment to filter projects by
     * @param contextUser - The current user context
     * @param forceRefresh - If true, reloads even if already cached for this environment
     */
    LoadProjects(environmentId: string, contextUser: UserInfo, forceRefresh?: boolean): Promise<void>;
    /**
     * Assigns a conversation to a folder (project), or removes it from its folder when
     * projectId is null. Thin wrapper over {@link SaveConversation} that keeps the
     * intent explicit at call sites.
     *
     * @param conversationId - The conversation to move
     * @param projectId - The target project ID, or null to ungroup
     * @param contextUser - The current user context
     * @returns true if saved successfully
     */
    MoveConversationToProject(conversationId: string, projectId: string | null, contextUser: UserInfo): Promise<boolean>;
    /**
     * Reparents a folder (project) under another folder, or to the top level when
     * parentId is null. Callers are responsible for preventing cycles (don't pass a
     * descendant of the folder as its new parent). Updates the cached entity in place
     * and re-emits Projects$.
     *
     * @param projectId - The folder to move
     * @param parentId - The new parent folder ID, or null for top level
     * @param contextUser - The current user context
     * @returns true if saved successfully
     */
    MoveProjectToParent(projectId: string, parentId: string | null, contextUser: UserInfo): Promise<boolean>;
    /**
     * Deletes a folder (project) in an FK-safe way. The Conversation→Project and
     * Project→Project (ParentID) foreign keys are RESTRICT, so the row can't be
     * deleted while anything references it. Before deleting, this:
     *   1. Unassigns every conversation directly in the folder (ProjectID → null).
     *   2. Reparents direct child folders to this folder's parent (one level up).
     *   3. Deletes the now-unreferenced folder.
     * Conversations and subfolders are preserved — only the folder itself is removed.
     *
     * Note: this does NOT reassign Tasks that reference the project; if a Task still
     * references it, the final delete will fail and this throws with the DB message.
     *
     * @param id - The project (folder) ID to delete
     * @param contextUser - The current user context
     * @returns true if deleted successfully
     */
    DeleteProject(id: string, contextUser: UserInfo): Promise<boolean>;
    /**
     * Creates a new conversation, saves it to the database, and adds it to the cached list.
     *
     * @param name - Display name for the conversation
     * @param environmentId - The environment ID
     * @param contextUser - The current user context
     * @param description - Optional description
     * @param projectId - Optional project ID
     * @returns The newly created conversation entity
     * @throws Error if save fails
     */
    CreateConversation(name: string, environmentId: string, contextUser: UserInfo, description?: string, projectId?: string, options?: CreateConversationOptions): Promise<MJConversationEntity>;
    /**
     * Folds a conversation that was created OUTSIDE this engine (e.g. server-side by a
     * realtime-session mint, which never fires a client BaseEntity event) into the cached
     * list so {@link Conversations$} emits reactively — the sidebar list updates without a
     * manual refresh. Costs at most ONE single-row query, and only when the conversation
     * isn't already cached:
     *
     *  - Already in the list → no-op (returns the cached entity).
     *  - Not cached → loads the single row, and (unless it's app-scoped, which lives
     *    outside the main-chat list — see {@link CreateConversation}) prepends it and
     *    re-emits. Returns the loaded entity, or null when the row can't be read.
     *
     * Idempotent and safe to call from a session-start hook on every start.
     *
     * @param id - The conversation ID to ensure is present in the cache
     * @param contextUser - The current user context
     * @returns The cached/loaded conversation entity, or null when it can't be loaded
     */
    EnsureConversationLoaded(id: string, contextUser: UserInfo): Promise<MJConversationEntity | null>;
    /**
     * Deletes a conversation from the database and removes it from the cached list.
     *
     * @param id - The conversation ID to delete
     * @param contextUser - The current user context
     * @returns true if successful
     * @throws Error if conversation not found or delete fails
     */
    DeleteConversation(id: string, contextUser: UserInfo): Promise<boolean>;
    /**
     * Archives a conversation (sets IsArchived = true) and removes it from the active list.
     *
     * @param id - The conversation ID to archive
     * @param contextUser - The current user context
     * @returns true if successful
     */
    ArchiveConversation(id: string, contextUser: UserInfo): Promise<boolean>;
    /**
     * Toggles or sets the pinned status of a conversation.
     *
     * @param id - The conversation ID
     * @param isPinned - Whether the conversation should be pinned
     * @param contextUser - The current user context
     * @returns true if successful
     */
    PinConversation(id: string, isPinned: boolean, contextUser: UserInfo): Promise<boolean>;
    /**
     * Finds a conversation by ID in the cached list.
     *
     * @param id - The conversation ID to find
     * @returns The conversation entity, or undefined if not in cache
     */
    GetConversation(id: string): MJConversationEntity | undefined;
    /**
     * Saves partial updates to a conversation (Name, Description, or any writable field).
     * Loads the entity from DB, applies updates, saves, and updates the in-memory list.
     *
     * @param id - The conversation ID to update
     * @param updates - Partial fields to update
     * @param contextUser - The current user context
     * @returns true if saved successfully
     * @throws Error if conversation not found or save fails
     */
    SaveConversation(id: string, updates: Partial<MJConversationEntity>, contextUser: UserInfo): Promise<boolean>;
    /**
     * Deletes multiple conversations in a batch operation with per-item error tracking.
     *
     * @param ids - Array of conversation IDs to delete
     * @param contextUser - The current user context
     * @returns Object with successful and failed deletions
     */
    DeleteMultipleConversations(ids: string[], contextUser: UserInfo): Promise<{
        Successful: string[];
        Failed: Array<{
            ID: string;
            Name: string;
            Error: string;
        }>;
    }>;
    /**
     * Loads conversation details (messages) for a specific conversation.
     * Results are cached for instant retrieval on subsequent calls.
     *
     * @param conversationId - The conversation to load details for
     * @param contextUser - The current user context
     * @param forceRefresh - If true, reloads even if cached
     * @returns Array of conversation detail entities
     */
    /**
     * Loads conversation details using the efficient GetConversationComplete query
     * which returns messages, agent runs, artifacts, ratings, and user avatars in one round-trip.
     * Results are cached for instant retrieval on subsequent calls.
     *
     * @param conversationId - The conversation to load details for
     * @param contextUser - The current user context
     * @param forceRefresh - If true, reloads even if cached
     * @returns The full cache entry with all peripheral data
     */
    LoadConversationDetails(conversationId: string, contextUser: UserInfo, forceRefresh?: boolean): Promise<ConversationDetailCache>;
    /**
     * Builds a full ConversationDetailCache from raw GetConversationComplete query results.
     * Hydrates entity objects and parses peripheral JSON data in one pass.
     */
    private buildDetailCacheFromRawData;
    /**
     * Refreshes conversation details by re-running the GetConversationComplete query
     * and surgically merging results into the existing cache. Existing objects that
     * haven't changed keep their references (minimizing Angular re-renders).
     *
     * - New messages: appended to Details array
     * - Existing messages: fields updated in-place on the same object
     * - Agent runs: updated in-place or added
     * - Artifacts/ratings: replaced per-detail (cheap — plain data, not entity objects)
     * - User avatars: merged (new users added)
     *
     * If no cache exists yet, falls back to a full load.
     *
     * @param conversationId - The conversation to refresh
     * @param contextUser - The current user context
     * @returns The updated cache entry
     */
    RefreshConversationDetails(conversationId: string, contextUser: UserInfo): Promise<ConversationDetailCache>;
    /**
     * Returns cached conversation details (messages only) without hitting the database.
     * Returns undefined if no cache entry exists for this conversation.
     *
     * @param conversationId - The conversation ID
     * @returns Cached message entities, or undefined if not cached
     */
    GetCachedDetails(conversationId: string): MJConversationDetailEntity[] | undefined;
    /**
     * Returns the full cache entry for a conversation, including all peripheral data
     * (agent runs, artifacts, ratings, user avatars). Returns undefined if not cached.
     *
     * This is the primary read method for UI components — returns instant cached data
     * without any database round-trip.
     *
     * @param conversationId - The conversation ID
     * @returns The full cache entry, or undefined
     */
    GetCachedDetailEntry(conversationId: string): ConversationDetailCache | undefined;
    /**
     * Returns true if conversation details are cached for the given conversation.
     */
    HasCachedDetails(conversationId: string): boolean;
    /**
     * Adds a detail (message) to the cached list for a conversation.
     * If no cache entry exists, this is a no-op (caller should LoadConversationDetails first).
     *
     * @param conversationId - The conversation this detail belongs to
     * @param detail - The detail entity to add
     */
    AddDetailToCache(conversationId: string, detail: MJConversationDetailEntity): void;
    /**
     * Updates a detail entity in the cache. Finds by ID and replaces.
     * If the detail is not in cache, this is a no-op.
     *
     * @param conversationId - The conversation this detail belongs to
     * @param detail - The updated detail entity
     */
    UpdateDetailInCache(conversationId: string, detail: MJConversationDetailEntity): void;
    /**
     * Gets the cached agent run for a specific conversation detail.
     *
     * @param conversationId - The conversation ID
     * @param detailId - The conversation detail ID
     * @returns The agent run entity, or undefined if not cached
     */
    GetAgentRunForDetail(conversationId: string, detailId: string): MJAIAgentRunEntity | undefined;
    /**
     * Gets all cached agent runs for a conversation, keyed by detail ID.
     *
     * @param conversationId - The conversation ID
     * @returns Map of detail ID to agent run, or empty map if not cached
     */
    GetAgentRunsMap(conversationId: string): Map<string, MJAIAgentRunEntity>;
    /**
     * Adds or updates an agent run in the cache for a specific detail.
     *
     * @param conversationId - The conversation ID
     * @param detailId - The detail ID the agent run is associated with
     * @param agentRun - The agent run entity
     */
    SetAgentRunForDetail(conversationId: string, detailId: string, agentRun: MJAIAgentRunEntity): void;
    /**
     * Invalidates (removes) the cached details for a specific conversation.
     * The next call to LoadConversationDetails will fetch fresh data.
     *
     * @param conversationId - The conversation ID to invalidate
     */
    InvalidateConversation(conversationId: string): void;
    /**
     * Clears all cached data: conversations, details, and peripheral data.
     * Typically called on logout or environment switch.
     */
    ClearCache(): void;
    /**
     * Creates a new conversation detail (message), saves it, and adds it to the cache.
     *
     * @param conversationId - The conversation this detail belongs to
     * @param role - The message role ('User', 'AI', 'System')
     * @param message - The message content
     * @param contextUser - The current user context
     * @param additionalFields - Optional extra fields to set on the entity
     * @returns The saved conversation detail entity
     */
    CreateConversationDetail(conversationId: string, role: MJConversationDetailEntity['Role'], message: string, contextUser: UserInfo, additionalFields?: Partial<MJConversationDetailEntity>): Promise<MJConversationDetailEntity>;
    /**
     * Saves an existing conversation detail entity and updates the cache.
     * Use this instead of calling detail.Save() directly to keep the engine cache in sync.
     *
     * @param detail - The conversation detail entity to save (must already be loaded)
     * @returns true if saved successfully
     */
    SaveConversationDetail(detail: MJConversationDetailEntity): Promise<boolean>;
    /**
     * Deletes a conversation detail and removes it from the cache.
     *
     * @param conversationId - The conversation this detail belongs to
     * @param detailId - The detail ID to delete
     * @param contextUser - The current user context
     * @returns true if deleted successfully
     */
    DeleteConversationDetail(conversationId: string, detailId: string, contextUser: UserInfo): Promise<boolean>;
    /**
     * Overrides BaseEngine's entity event handler to watch for external mutations
     * to Conversations and Conversation Details. When another piece of code (outside
     * this engine) saves or deletes these entities, we sync our cache.
     *
     * The _selfMutating guard prevents processing events from our own mutations.
     */
    protected HandleIndividualBaseEntityEvent(event: BaseEntityEvent): Promise<boolean>;
    /**
     * Extracts record data from a BaseEntityEvent.
     * For local events: uses baseEntity directly.
     * For remote-invalidate events: parses recordData JSON from the payload.
     * Returns null if no data is available.
     */
    private extractRecordData;
    /**
     * Safely merges data onto a target object that may or may not be a BaseEntity.
     * If the target has SetMany (i.e., it's a BaseEntity), uses that to properly handle
     * read-only fields like __mj_CreatedAt. Otherwise falls back to Object.assign.
     */
    private mergeDataOntoRecord;
    /**
     * Handles save/delete events on Conversation entities from local or remote code.
     */
    private handleConversationEntityEvent;
    /**
     * Handles save/delete events on ConversationDetail entities from local or remote code.
     */
    private handleConversationDetailEntityEvent;
    /**
     * Handles save/delete events on Project entities from local or remote code.
     * Keeps the folder list in sync when a folder is created, renamed, archived, or
     * deleted via the project form modal. Only tracks projects in the currently-loaded
     * environment; archived projects are dropped from the active list.
     */
    private handleProjectEntityEvent;
    /**
     * Handles save/delete events on AI Agent Run entities from local or remote code.
     * Updates the AgentRunsByDetailId map so timers and status reflect reality.
     */
    private handleAgentRunEntityEvent;
    /**
     * Handles save/delete events on junction entities (Conversation Detail Artifacts,
     * Conversation Detail Ratings) from local or remote code.
     *
     * These entities have joined fields (ArtifactName, UserName, etc.) that can't be
     * reconstructed from the entity event alone, so we flag the cache as stale.
     * The UI component checks PeripheralDataStale and force-refreshes when needed.
     */
    private handlePeripheralJunctionEntityEvent;
    /**
     * Saves a partial update to a conversation entity.
     */
    private saveConversationField;
    /**
     * Removes a conversation from the in-memory list and emits the updated list.
     */
    private removeFromList;
    /**
     * Removes multiple IDs from the cached list in a single emission.
     * Used by DeleteMultipleConversations for a smooth batch UI update.
     */
    private removeMultipleFromList;
    /**
     * Removes the detail cache entry for a conversation.
     */
    private removeDetailCache;
    /**
     * Sorts conversations: pinned first, then by updated date descending.
     */
    private sortConversations;
}
//# sourceMappingURL=conversations.d.ts.map