import type { FormaElement, Properties, Transform, Urn } from "forma-elements";
import type { IframeMessenger } from "./iframe-messenger.js";
export type ProposalElement = FormaElement & {
    properties?: Properties & {
        category?: "proposal";
        name?: string;
        flags: Record<string, {
            scenario?: boolean;
        }>;
    };
};
/**
 * Access proposal metadata and add new elements to it.
 *
 * @remarks
 * Available via {@link auto.Forma | Forma}.{@link index.EmbeddedViewSdk.proposal | proposal}.
 */
export declare class ProposalApi {
    #private;
    /** @hidden */
    constructor(iframeMessenger: IframeMessenger);
    /**
     * Fetch the top level URN for the proposal.
     *
     * @returns Root URN
     *
     * @example
     * const rootUrn = await Forma.proposal.getRootUrn()
     */
    getRootUrn(): Promise<Urn>;
    /**
     * Fetch the unique identifier of the proposal.
     *
     * @returns Proposal ID
     *
     * @example
     * const proposalId = await Forma.proposal.getId()
     */
    getId(): Promise<string>;
    /**
     * Add a new element to the proposal element tree.
     *
     * Requires edit access. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
     *
     * @returns { path: string } object with the path of the new element
     *
     * @example
     * const urn = mockRegisterElementInSystem() // See e.g. integrate-elements module
     * const { path } = await Forma.proposal.addElement({ urn })
     */
    addElement(request: {
        /**
         * URN of the element to add.
         */
        urn: string;
        /**
         * Path to element whose children the new element should be added to.
         *
         * Currently the only supported parents are
         * - top-level of proposal (`"root"`)
         * - a base layer (`"root/<base key>"`)
         *
         * Defaults to "root".
         */
        parentPath?: string;
        /**
         * Flattened list of a standard 4x4 affine transform matrix in column-major order, to position the element relative to its parent.
         *
         * When the parent is proposal root, the project reference point is used as origin.
         *
         * Defaults to equivalent of 4x4 identity matrix.
         */
        transform?: number[];
        /**
         * Name of the element.
         */
        name?: string;
    }): Promise<{
        path: string;
    }>;
    /**
     * Replace an element in the proposal.
     *
     * Requires edit access. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
     *
     * @example
     * const urn = mockRegisterElementInSystem() // See e.g. integrate-elements module
     * const { path } = await Forma.proposal.addElement({ urn })
     * const newUrn = mockRegisterElementInSystem() // See e.g. integrate-elements module
     * await Forma.proposal.replaceElement({ path, newUrn })
     */
    replaceElement(request: {
        /**
         * Path of the element you want to replace.
         *
         * The path is relative to the proposal element and starts with "root/".
         *
         * Currently, the only supported parents are
         * - top-level of proposal (`"root"`)
         * - a base layer (`"root/<base key>"`)
         *
         * This will be the same value as the path returned from {@link ProposalApi.addElement | addElement}.
         */
        path: string;
        /**
         * URN of the new element.
         */
        urn: string;
    }): Promise<void>;
    /**
     * Remove an element in the proposal.
     *
     * Requires edit access. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
     *
     * @example
     * Remove the first of the selected elements
     * ```ts
     * const selection = await Forma.selection.getSelection()
     * if (selection.length > 0) {
     *    await Forma.proposal.removeElement({ path: selection[0] })
     * }
     * ```
     */
    removeElement(request: {
        /**
         * Path of the element you want to remove.
         *
         * The path is relative to the proposal element and starts with "root/".
         *
         * Currently, the only supported parents are
         * - top-level of proposal (`"root"`)
         * - a base layer (`"root/<base key>"`)
         *
         * This will be the same value as the path returned from {@link ProposalApi.addElement | addElement}.
         */
        path: string;
    }): Promise<void>;
    /**
     * Replace existing terrain on the proposal.
     *
     * Requires edit access. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
     *
     * @remarks
     * This method is not supported for non-internal terrains.
     * Use {@link TerrainApi.isInternal | Forma.terrain.isInternal} to check whether
     * the terrain is internal before calling this method.
     *
     * @example
     * const glb = createGlbSomehow()
     * await Forma.proposal.replaceTerrain({ glb })
     */
    replaceTerrain(request: {
        /** GLB file. */
        glb: ArrayBuffer;
    }): Promise<void>;
    /**
     * Executes a batch of element operations (`add`, `replace`, `remove`) within a single proposal.
     * For more info see {@link ProposalApi.addElement | addElement}, {@link ProposalApi.replaceElement | replaceElement} and {@link ProposalApi.removeElement | removeElement}.
     *
     * This method allows for efficient bulk modifications of elements by combining multiple add, replace, and/or remove operations into a single call.
     * Operations are performed in the sequence they are provided. This method is useful for applying complex changes to a scene with minimal requests.
     *
     * Note: This operation requires edit access. Use {@link EmbeddedViewSdk.getCanEdit | getCanEdit} to verify edit permissions.
     *
     * @param request - An array of objects detailing the operations to be performed.
     * Each object should specify the `operation` type (`"add"`, `"replace"`, `"remove"`) and the required parameters.
     *
     * @returns A Promise that resolves to an array of results, one for each operation. For added elements, the result includes the new element path.
     *
     * @example
     * // Example: Adding three new elements to the scene
     * const urn1 = mockRegisterElementInSystem(); // Register a new element and obtain its urn
     * const urn2 = mockRegisterElementInSystem(); // Register another new element
     * const urn3 = mockRegisterElementInSystem(); // Register another new element
     *
     * const addElementOperations = [
     *   { type: "add", urn: urn1 },
     *   { type: "add", urn: urn2 },
     *   { type: "add", urn: urn3 },
     * ];
     *
     * // Example: Modifying three existing elements in the scene i.e. replacing the element at the first path
     * // with the second element and removing the elements at the second and third paths
     * const results = await Forma.proposal.updateElements({ operations: addElementOperations })
     * const paths = results.map((result) => result?.path);
     * console.log(`Added elements at paths: ${paths.join(", ")}`);
     *
     * const modifyElementOperations = [
     *   { type: "replace", urn: urn2, path: paths[0] },
     *   { type: "remove", path: paths[1] },
     *   { type: "remove", path: paths[2] },
     * ];
     *
     * await Forma.proposal.updateElements({ operations: modifyElementOperations })
     *
     * @throws {Error} If the user does not have edit access, provides invalid paths or attempts to modify terrain element.
     */
    updateElements(request: {
        operations: ({
            type: "add";
            /**
             * Path to element whose children the new element should be added to.
             *
             * Currently, the only supported parents are
             * - top-level of proposal (`"root"`)
             * - a base layer (`"root/<base key>"`)
             */
            parentPath?: string;
            /**
             * URN of the element to add.
             */
            urn: string;
            /**
             * Name of the element.
             */
            name?: string;
            /**
             * Flattened list of a standard 4x4 affine transform matrix in column-major order, to position the element relative to its parent.
             */
            transform?: number[];
        } | {
            type: "replace";
            /**
             * Path of the element to replace.
             *
             * Currently, the only supported paths are
             * - top-level of proposal (`"root/<key>"`)
             * - a base layer (`"root/<base key>/<key>"`)
             */
            path: string;
            /**
             * URN of the new element.
             */
            urn: string;
        } | {
            type: "remove";
            /**
             * Path of the element to remove.
             *
             * Currently, the only supported paths are
             * - top-level of proposal (`"root/<key>"`)
             * - a base layer (`"root/<base key>/<key>"`)
             */
            path: string;
        })[];
    }): Promise<({
        path: string;
    } | null)[]>;
    /**
     * Subscribe to changes in the proposal.
     *
     * By default this will be called for every change in the proposal, including changes that might not be persisted as a separate change.
     *
     * This can be changed by setting the `debouncedPersistedOnly` option to true, in which case the callback will only be called after the proposal is persisted as well.
     * If multiple changes are made in quick succession, the callback will be called only once for all changes.
     *
     * @example
     * Forma.proposal.subscribe(
     *   async ({ rootUrn }) => {
     *     console.log(`Proposal ${rootUrn} has been created and persisted`)
     *   },
     *   {
     *     debouncedPersistedOnly: true,
     *   },
     * )
     * @param callback event handler for each proposal change
     * @param options.debouncedPersistedOnly - if true, the callback will be called only after the proposal is persisted.
     * @returns { unsubscribe } - Object with an `unsubscribe` function to stop listening to events.
     */
    subscribe(callback: (payload: {
        rootUrn: string;
    }) => void, options?: {
        debouncedPersistedOnly?: boolean;
    }): Promise<{
        unsubscribe: () => void;
    }>;
    /**
     * This function is resolved when the currently loaded proposal is properly persisted in the system.
     * The underlying host application can operate optimistically and return values before the proposal is persisted.
     * While other APIs require the proposal to be persisted before they can be used. Examples of such APIs are the elements API.
     *
     * If you subscribe to proposal updates, you should likely instead look at the `debouncedPersistedOnly` option in the `subscribe` function.
     */
    awaitProposalPersisted(): Promise<void>;
    /**
     * This will get all proposals belongs to the current project.
     *
     * @returns ProposalElement[]
     *
     * @example
     * const proposals = await Forma.proposal.getAll()
     */
    getAll(): Promise<ProposalElement[]>;
    /**
     * This will get the proposal with the given ID and/or revision.
     *
     * @returns ProposalElement
     *
     * @example
     * const proposalId = await Forma.proposal.getId()
     * const proposal = await Forma.proposal.get({ proposalId: proposalId })
     */
    get(request: {
        proposalId: string;
        revision?: string;
    }): Promise<ProposalElement>;
    /**
     * This will create a new proposal with the given name, terrain, base, and children.
     *
     * @returns { urn: Urn } object with the URN of the new proposal
     *
     * @example
     * const newProposal = await Forma.proposal.create({
     *  name: "New Proposal",
     *  terrain: { urn: "urn:adsk-forma-elements:terrain:projectId:elementId:revision", key: "<key>" },
     *  base: { urn: "urn:adsk-forma-elements:base:projectId:elementId:revision", key: "<key>" },
     *  children: [],
     * })
     */
    create(request: {
        name: string;
        terrain: {
            urn: Urn;
            key: string;
            transform?: Transform;
            name?: string;
        };
        base: {
            urn: Urn;
            key: string;
            transform?: Transform;
            name?: string;
        };
        children: {
            urn: Urn;
            key: string;
            transform?: Transform;
            name?: string;
        }[];
    }): Promise<{
        urn: Urn;
    }>;
    /**
     * This will update the proposal with the given ID and revision.
     *
     * @returns { urn: Urn } object with the URN of the updated proposal
     *
     * @example
     * const proposalId = await Forma.proposal.getId()
     * const updatedProposal = await Forma.proposal.update({
     * proposalId: proposalId,
     * revision: "revisionId",
     * proposal: {
     *    name: "Updated Proposal",
     *    terrain: { urn: "urn:adsk-forma-elements:terrain:projectId:elementId:revision", key: "<key>" },
     *    base: { urn: "urn:adsk-forma-elements:base:projectId:elementId:revision", key: "<key>" },
     *    children: [],
     *  }
     * })
     */
    update(request: {
        proposalId: string;
        revision: string;
        proposal: {
            name?: string;
            terrain: {
                urn: Urn;
                key: string;
                transform?: Transform;
                name?: string;
            };
            base: {
                urn: Urn;
                key: string;
                transform?: Transform;
                name?: string;
            };
            children: {
                urn: Urn;
                key: string;
                transform?: Transform;
                name?: string;
            }[];
        };
    }): Promise<{
        urn: Urn;
    }>;
    /**
     * This will delete the proposal with the given ID. And load the next available proposal in the canvas.
     *
     * @example
     * const proposalId = await Forma.proposal.getId()
     * await Forma.proposal.delete({
     *  proposalId: proposalId,
     * })
     *
     * @remarks
     * This is a soft delete, meaning the proposal can be restored.
     *
     * @experimental
     */
    delete(request: {
        proposalId: string;
    }): Promise<void>;
    /**
     * This will create a new proposal with the same elements as the original.
     *
     * @experimental
     *
     * @returns { urn: Urn } object with the URN of the duplicated proposal
     *
     * @example
     * const proposalId = await Forma.proposal.getId()
     * const duplicatedProposal = await Forma.proposal.duplicate({
     *  proposalId: proposalId,
     * })
     */
    duplicate(request: {
        proposalId: string;
        revision?: string;
    }): Promise<ProposalElement>;
    /**
     * Switch to a different proposal.
     *
     * @example
     * const proposals = await Forma.proposal.getAll()
     * await Forma.proposal.switch({
     *  proposalId: proposals[0].urn.split(":")[4],
     *  revision: proposals[0].urn.split(":")[5],
     * })
     *
     * @remarks
     * This will switch to the proposal with the given ID and/or revision.
     * If your extension loads data from the proposal or operates on the current proposal in any way,
     * you should listen to the `proposal change` event using the {@link subscribe} method to know when the proposal has been switched.
     * Otherwise, you might be operating on the wrong proposal or the proposal contextual data might be incorrect.
     */
    switch(request: {
        proposalId: string;
        revision?: string;
    }): Promise<void>;
}
