/**
 * Generic undo stack with clone-on-push semantics.
 *
 * Stores deep clones of state snapshots. Popped snapshots are returned
 * directly (no re-cloning) since they are already detached.
 */
export class UndoStack<S> {
	private stack: S[] = [];
	private redoStack: S[] = [];

	/** Push a deep clone of the given state onto the undo stack. Clears redo history. */
	push(state: S): void {
		this.stack.push(structuredClone(state));
		this.redoStack.length = 0;
	}

	/** Pop and return the most recent undo snapshot, or undefined if empty. */
	pop(): S | undefined {
		return this.stack.pop();
	}

	/**
	 * Push a deep clone of the given state onto the redo stack.
	 * Called by the editor before applying an undo so the state can be redone.
	 */
	pushRedo(state: S): void {
		this.redoStack.push(structuredClone(state));
	}

	/**
	 * Push a deep clone of the given state onto the undo stack without clearing
	 * the redo stack. Called by the editor before applying a redo so the state
	 * remains undoable.
	 */
	pushUndo(state: S): void {
		this.stack.push(structuredClone(state));
	}

	/** Pop and return the most recent redo snapshot, or undefined if empty. */
	redo(): S | undefined {
		return this.redoStack.pop();
	}

	canRedo(): boolean {
		return this.redoStack.length > 0;
	}

	/** Remove all snapshots from both undo and redo stacks. */
	clear(): void {
		this.stack.length = 0;
		this.redoStack.length = 0;
	}

	get length(): number {
		return this.stack.length;
	}
}
