/**
 * Per-instance Zustand store for the Editors component.
 *
 * ARCHITECTURE NOTE — the store owns the live editing buffer:
 * `createEditorStore(init)` seeds the editing buffer (`divisions`, `title`,
 * `docinfo`, `activeDivisionId`, …) from the host's initial props *once*.
 * After that, the store is authoritative for what's being edited:
 *   • Internal edit actions (`setDivisionContent`, `patchDivision`, `setTitle`,
 *     …) update the store optimistically and the host callbacks are fired
 *     purely as notifications (so the host can persist/autosave).  A host is no
 *     longer required to echo every edit back as new props for it to display.
 *   • Genuine external updates (a save that reconciles server-assigned ids, or
 *     swapping to a different project) still win: Editors.tsx detects when a
 *     controlled prop actually changes since the last render and calls
 *     `applyExternalUpdate()` to overwrite the buffer.  A stale prop that the
 *     host simply never updated is NOT re-applied, so it can't clobber a local
 *     edit.
 *
 * Derived/config fields that are never edited locally (`source`, `sourceFormat`,
 * `projectAssets`, `projectType`, `rootDivisionId`, …) are still mirrored from
 * props every render via `syncState()`.
 *
 * Callback stability: createEditorStore returns a `bindCallbacks` function that
 * EditorsInner calls from useLayoutEffect after every render. Store actions
 * close over an internal mutable bag (`bag.cbs`) rather than React refs, so
 * they are stable while always calling the latest mode-routed callback.
 */
import { type StoreApi } from "zustand/vanilla";
import type { Asset, AssetKind, FeedbackSubmission, SourceFormat } from "../types/editor";
import type { Division, DivisionType } from "../types/sections";
import type { EditDraft } from "../components/toc/types";
export type DivisionChanges = {
    id?: string;
    title?: string;
    type?: DivisionType;
    xmlId?: string | null;
    sourceFormat?: SourceFormat;
    label?: string | null;
};
/**
 * A batch of editing-buffer fields the host has genuinely changed (an external
 * reset).  Only the provided fields are overwritten in the store; omitted
 * fields keep their current — possibly locally edited — value.
 */
export interface ExternalUpdate {
    divisions?: Division[];
    projectAssets?: Asset[];
    rootDivisionId?: string;
    activeDivisionId?: string | null;
    title?: string;
    docinfo?: string;
    commonDocinfo?: string;
    useCommonDocinfo?: boolean;
}
type ModalKey = "isLatexDialogOpen" | "isConvertDialogOpen" | "isDocinfoEditorOpen" | "isAssetPickerOpen";
/**
 * All callbacks wired by Editors.tsx that deep components need to call.
 * Updated on every render via `callbacksRef.current = { ... }`.
 * Actions in the store call through this ref, so they stay stable even as
 * the callbacks close over changing state.
 */
export interface EditorCallbacks {
    selectDivision: (id: string) => void;
    addDivision: (afterId: string | null) => void;
    removeDivision: (id: string) => void;
    updateDivision: (id: string, changes: DivisionChanges) => void;
    /** Emit a content change for a specific division (edit or structural reorder). */
    divisionContentChange: (xmlId: string, content: string) => void;
    handleDivisionContentChange: (content: string | undefined) => void;
    assetInsert: (asset: Asset) => void;
    updateTitle: (title: string) => void;
    feedbackSubmit?: (feedback: FeedbackSubmission) => void | Promise<void>;
    insertContentAtCursor?: (content: string) => void;
}
export interface EditorStoreState {
    source: string;
    sourceFormat: SourceFormat;
    /**
     * Authoritative project-asset pool — owned by the store as a live editing
     * buffer, exactly like {@link EditorStoreState.divisions}. Seeded once from
     * the host's `projectAssets` prop, then mutated optimistically by
     * `addAssetToPool`/`updateAssetInPool`/`removeAssetFromPool` (host callbacks
     * fire purely as persistence notifications). A genuine external change to the
     * prop wins via `applyExternalUpdate`, but a stale prop the host never updated
     * can't clobber a just-created asset — so an asset is editable the instant
     * it's added, without waiting for the host to echo it back.
     */
    projectAssets: Asset[] | undefined;
    /**
     * The user's cross-project asset library. Unlike `projectAssets` this stays a
     * host-owned, fetch-on-demand cache (loaded via `onLoadLibraryAssets`), so it
     * is still mirrored from props every render via `syncState`.
     */
    libraryAssets: Asset[] | undefined;
    title: string;
    docinfo: string;
    commonDocinfo: string;
    useCommonDocinfo: boolean;
    projectType: "article" | "book" | undefined;
    projectUrl: string | undefined;
    divisions: Division[] | undefined;
    rootDivisionId: string | undefined;
    activeDivisionId: string | null;
    canConvertToPretext: boolean;
    /** The source string currently open in the code editor. */
    activeEditorSource: string;
    /** True when the host passed `onFeedbackSubmit`. Controls whether feedback UI is shown. */
    hasFeedback: boolean;
    isTocCollapsed: boolean;
    showFullPreview: boolean;
    isNarrowScreen: boolean;
    activeTab: "editor" | "preview";
    isLatexDialogOpen: boolean;
    isConvertDialogOpen: boolean;
    isDocinfoEditorOpen: boolean;
    isAssetPickerOpen: boolean;
    editingId: string | null;
    editDraft: EditDraft | null;
    /** The asset currently open in the asset edit modal, identified by kind+ref. */
    editingAssetRef: {
        kind: AssetKind;
        ref: string;
    } | null;
    /** Sync a batch of derived/controlled data from Editors into the store. */
    syncState: (partial: Partial<EditorSyncableState>) => void;
    /** Apply a genuine external update from the host (host wins). */
    applyExternalUpdate: (partial: ExternalUpdate) => void;
    /** Optimistically set a division's content in the local pool. */
    setDivisionContent: (xmlId: string, content: string) => void;
    /** Optimistically patch a division's metadata (title/type/xml:id/format). */
    patchDivision: (xmlId: string, changes: DivisionChanges) => void;
    /** Optimistically add a division to the local pool (no-op if it exists). */
    addDivisionToPool: (division: Division) => void;
    /** Optimistically remove a division from the local pool. */
    removeDivisionFromPool: (xmlId: string) => void;
    /** Set the active (open-for-editing) division id. */
    setActiveDivisionId: (id: string | null) => void;
    /** Optimistically set the document title. */
    setTitle: (title: string) => void;
    /** Optimistically set the docinfo-related fields together. */
    setDocinfo: (info: {
        docinfo: string;
        commonDocinfo: string;
        useCommonDocinfo: boolean;
    }) => void;
    setShowFullPreview: (show: boolean) => void;
    setActiveTab: (tab: "editor" | "preview") => void;
    setIsNarrowScreen: (narrow: boolean) => void;
    setIsTocCollapsed: (value: boolean | ((prev: boolean) => boolean)) => void;
    openModal: (modal: ModalKey) => void;
    closeModal: (modal: ModalKey) => void;
    selectSection: (id: string) => void;
    addSection: (afterId: string | null) => void;
    removeSection: (id: string) => void;
    updateSection: (id: string, changes: DivisionChanges) => void;
    /** Update a parent division's content after a structural DnD change. */
    divisionContentChange: (xmlId: string, content: string) => void;
    startSectionEdit: (section: Division) => void;
    setEditDraft: (draft: EditDraft) => void;
    commitSectionEdit: () => void;
    cancelSectionEdit: () => void;
    insertAsset: (asset: Asset) => void;
    insertAtCursor: (content: string) => void;
    /** Open the asset edit modal for the asset identified by `kind`+`ref`. */
    openAssetEditor: (kind: AssetKind, ref: string) => void;
    closeAssetEditor: () => void;
    /**
     * Replace the whole project-asset pool — e.g. after `onLoadAssets` resolves
     * with the server's fresh list. The server is authoritative at that point,
     * so this overwrites the pool wholesale (matching how the divisions pool is
     * reset on an external update).
     */
    setProjectAssets: (assets: Asset[]) => void;
    /**
     * Optimistically add an asset to the pool (no-op if one with the same
     * kind+ref already exists). Used when an asset is uploaded, created, added
     * from the library, or inserted, so it's editable immediately.
     */
    addAssetToPool: (asset: Asset) => void;
    /**
     * Optimistically replace the pool entry matching `asset` by kind+ref (adding
     * it if absent). Used when an asset's content/source is edited.
     */
    updateAssetInPool: (asset: Asset) => void;
    /** Optimistically remove the asset matching `asset` by kind+ref from the pool. */
    removeAssetFromPool: (asset: Asset) => void;
    updateTitle: (title: string) => void;
    feedbackSubmit: (feedback: FeedbackSubmission) => void;
}
/** The subset of EditorStoreState that Editors.tsx syncs on each render. */
export type EditorSyncableState = Pick<EditorStoreState, "source" | "sourceFormat" | "libraryAssets" | "title" | "docinfo" | "commonDocinfo" | "useCommonDocinfo" | "projectType" | "projectUrl" | "divisions" | "rootDivisionId" | "activeDivisionId" | "canConvertToPretext" | "activeEditorSource" | "hasFeedback">;
export interface EditorStoreInit {
    source: string;
    sourceFormat: SourceFormat;
    title: string;
    docinfo: string;
    commonDocinfo: string;
    useCommonDocinfo: boolean;
    projectType: "article" | "book" | undefined;
    divisions: Division[];
    activeDivisionId: string | null;
    projectAssets: Asset[] | undefined;
}
/** The Zustand vanilla store instance type. */
export type EditorStoreInstance = StoreApi<EditorStoreState>;
/** Return value of createEditorStore. */
export interface EditorStoreHandle {
    /** The Zustand vanilla store — pass to EditorStoreProvider. */
    store: EditorStoreInstance;
    /**
     * Update the mutable callbacks bag.  Call from useLayoutEffect after every
     * render so store actions always invoke the latest mode-routed callbacks.
     */
    bindCallbacks: (cbs: EditorCallbacks) => void;
}
export declare function createEditorStore(init: EditorStoreInit): EditorStoreHandle;
export {};
