import { create } from "zustand";
import Konva from "konva";
import {
  buildGroupResult,
  createInitialCells,
  createInitialPage,
  createScopedId,
  createTableCellId,
  isInvalidPageId,
  reorderByIds,
  applyDefaultPageNames,
  ungroupById,
  normalizePageObjects,
} from "./editorStore.helpers";
import type {
  EditorObject,
  Page,
  PageGuide,
  TableCell,
  TableData,
  ToolType,
  DocumentSettings,
  GalleryItem,
} from "./editorStore.types";

const MAX_OBJECT_HISTORY = 50;

/** اسنپ‌شات آرایٔهٔ اشیا برای undo؛ از انتشار مرج مشترک جلوگیری می‌شود */
const cloneObjectList = (objects: EditorObject[]): EditorObject[] =>
  structuredClone(objects);

const objectListsEqual = (a: EditorObject[], b: EditorObject[]) =>
  JSON.stringify(a) === JSON.stringify(b);

/** یک آرایهٔ objects برای state و صفحهٔ جاری در pages (جلوگیری از از دست رفتن فیلدهایی مثل entranceAnimation) */
const withSyncedCurrentPageObjects = (
  state: { objects: EditorObject[]; pages: Page[]; currentPageId: string | null },
  nextObjects: EditorObject[],
) => {
  if (!state.currentPageId) {
    return { objects: nextObjects };
  }
  return {
    objects: nextObjects,
    pages: state.pages.map((p) =>
      p.id === state.currentPageId ? { ...p, objects: nextObjects } : p,
    ),
  };
};

let objectGestureSnapshot: EditorObject[] | null = null;

export type {
  EditorObject,
  Page,
  PageGuide,
  TableCell,
  TableData,
  ToolType,
  DocumentSettings,
  GalleryItem,
  DisplayStyle,
  BackgroundType,
  EntranceAnimationType,
} from "./editorStore.types";

type PageBackgroundSettings = Pick<
  Page,
  | "backgroundType"
  | "backgroundColor"
  | "backgroundGradient"
  | "backgroundImageUrl"
  | "backgroundVideoUrl"
>;

type EditorStoreType = {
  tool: ToolType;
  setTool: (tool: ToolType) => void;
  objects: EditorObject[];
  guides: PageGuide[];
  addObject: (object: EditorObject) => void;
  setGuides: (guides: PageGuide[]) => void;
  updateObject: (id: string, updates: Partial<EditorObject>) => void;
  deleteObject: (id: string, options?: { skipHistory?: boolean }) => void;
  duplicateObject: (id: string) => void;
  copySelectedObjects: () => void;
  pasteCopiedObjects: () => void;
  reorderObjects: (draggedId: string, targetId: string) => void;
  selectedObjectId: string | null;
  setSelectedObjectId: (id: string | null) => void;
  selectedObjectIds: string[];
  setSelectedObjectIds: (ids: string[]) => void;
  addToSelection: (id: string) => void;
  removeFromSelection: (id: string) => void;
  selectedTableId: string | null;
  setSelectedTableId: (id: string | null) => void;
  selectedCellId: string | null;
  setSelectedCellId: (id: string | null) => void;
  editingCell: {
    tableId: string;
    cellId: string;
    x: number;
    y: number;
    width: number;
    height: number;
    value: string;
  } | null;
  setEditingCell: (
    cell: {
      tableId: string;
      cellId: string;
      x: number;
      y: number;
      width: number;
      height: number;
      value: string;
    } | null,
  ) => void;
  addTable: (x: number, y: number, rows?: number, cols?: number) => void;
  addRow: (tableId: string) => void;
  addColumn: (tableId: string) => void;
  removeRow: (tableId: string) => void;
  removeColumn: (tableId: string) => void;
  updateCellValue: (tableId: string, cellId: string, text: string) => void;
  changeCellBackground: (
    tableId: string,
    cellId: string,
    color: string,
  ) => void;
  applyTableResize: (
    tableId: string,
    newWidth: number,
    newHeight: number,
  ) => void;
  stageRef: React.RefObject<Konva.Stage | null> | null;
  setStageRef: (ref: React.RefObject<Konva.Stage | null>) => void;
  layerRef: React.RefObject<Konva.Layer | null> | null;
  setLayerRef: (ref: React.RefObject<Konva.Layer | null>) => void;
  zoom: number;
  setZoom: (zoom: number) => void;
  zoomIn: () => void;
  zoomOut: () => void;
  pages: Page[];
  currentPageId: string | null;
  addPage: (name?: string) => void;
  duplicatePage: (pageId: string) => void;
  deletePage: (pageId: string) => void;
  reorderPages: (draggedId: string, targetId: string) => void;
  setCurrentPage: (pageId: string) => void;
  updatePageName: (pageId: string, name: string) => void;
  toggleObjectVisibility: (id: string) => void;
  groupObjects: (objectIds: string[]) => void;
  ungroupObjects: (groupId: string) => void;
  loadPages: (
    pages: Page[],
    documentSettings?: Partial<DocumentSettings>,
    gallery?: GalleryItem[],
  ) => void;
  resetEditor: () => void;
  clipboardObjects: EditorObject[];
  objectHistoryPast: EditorObject[][];
  objectHistoryFuture: EditorObject[][];
  commitObjectHistoryBeforeChange: () => void;
  beginObjectGesture: () => void;
  endObjectGesture: () => void;
  undoObjects: () => void;
  redoObjects: () => void;
  bringObjectForward: (id: string) => void;
  sendObjectBackward: (id: string) => void;
  documentSettings: DocumentSettings;
  updateDocumentSettings: (updates: Partial<DocumentSettings>) => void;
  updateCurrentPageBackground: (
    updates: Partial<PageBackgroundSettings>,
  ) => void;
  gallery: GalleryItem[];
  addGalleryItem: (item: GalleryItem) => void;
  removeGalleryItem: (id: string) => void;
  setGallery: (items: GalleryItem[]) => void;
};

const DEFAULT_DOCUMENT_SETTINGS: DocumentSettings = {
  displayStyle: "double",
  autoPlay: false,
  pageFlipSound: true,
  leftToRightFlip: false,
  smartGuide: true,
  backgroundType: "color",
  backgroundColor: "#ffffff",
  backgroundGradient: {
    from: "#ffffff",
    to: "#e2e8f0",
    angle: 135,
  },
  backgroundImageUrl: "",
  backgroundVideoUrl: "",
};

export const useEditorStore = create<EditorStoreType>((set, get) => {
  const initialPage = createInitialPage("جلد");
  const cloneObjectsForPaste = (
    sourceObjects: EditorObject[],
    offset = 20,
  ): EditorObject[] => {
    if (sourceObjects.length === 0) return [];

    const sourceIds = new Set(sourceObjects.map((obj) => obj.id));
    const idMap = new Map<string, string>();
    sourceObjects.forEach((obj) => {
      idMap.set(obj.id, createScopedId(obj.id));
    });

    return sourceObjects.map((objectToClone) => ({
      ...objectToClone,
      id: idMap.get(objectToClone.id)!,
      x: (objectToClone.x || 0) + offset,
      y: (objectToClone.y || 0) + offset,
      groupId:
        objectToClone.groupId && sourceIds.has(objectToClone.groupId)
          ? idMap.get(objectToClone.groupId)
          : undefined,
      ...(objectToClone.tableData && {
        tableData: {
          ...objectToClone.tableData,
          id: createScopedId("table"),
          cells: Object.fromEntries(
            Object.entries(objectToClone.tableData.cells).map(([key, cell]) => [
              key,
              {
                ...cell,
                id: createScopedId("cell"),
              },
            ]),
          ),
        },
      }),
    }));
  };

  return {
    documentSettings: { ...DEFAULT_DOCUMENT_SETTINGS },
    updateDocumentSettings: (updates) =>
      set((state) => ({
        documentSettings: { ...state.documentSettings, ...updates },
      })),
    updateCurrentPageBackground: (updates) =>
      set((state) => {
        if (!state.currentPageId) return {};
        const currentPage = state.pages.find(
          (p) => p.id === state.currentPageId,
        );
        if (!currentPage) return {};
        const nextPage = { ...currentPage, ...updates };
        return {
          pages: state.pages.map((p) =>
            p.id === state.currentPageId ? nextPage : p,
          ),
        };
      }),
    gallery: [],
    addGalleryItem: (item) =>
      set((state) => ({ gallery: [...state.gallery, item] })),
    removeGalleryItem: (id) =>
      set((state) => ({
        gallery: state.gallery.filter((item) => item.id !== id),
      })),
    setGallery: (items) => set({ gallery: items }),
    tool: "select",
    setTool: (tool) =>
      set((state) => ({
        tool,
        selectedObjectId: tool === "select" ? state.selectedObjectId : null,
      })),
    objects: [],
    guides: [],
    clipboardObjects: [],
    objectHistoryPast: [],
    objectHistoryFuture: [],
    commitObjectHistoryBeforeChange: () => {
      const s = get();
      const snap = cloneObjectList(s.objects);
      set({
        objectHistoryPast: [...s.objectHistoryPast, snap].slice(
          -MAX_OBJECT_HISTORY,
        ),
        objectHistoryFuture: [],
      });
    },
    beginObjectGesture: () => {
      objectGestureSnapshot = cloneObjectList(get().objects);
    },
    endObjectGesture: () => {
      const base = objectGestureSnapshot;
      objectGestureSnapshot = null;
      if (!base) return;
      if (objectListsEqual(base, get().objects)) return;
      const s = get();
      set({
        objectHistoryPast: [...s.objectHistoryPast, base].slice(
          -MAX_OBJECT_HISTORY,
        ),
        objectHistoryFuture: [],
      });
    },
    undoObjects: () => {
      const s = get();
      if (s.objectHistoryPast.length === 0) return;
      const previous = s.objectHistoryPast[s.objectHistoryPast.length - 1]!;
      const newPast = s.objectHistoryPast.slice(0, -1);
      const currentSnap = cloneObjectList(s.objects);
      const next = cloneObjectList(previous);
      const idSet = new Set(next.map((o) => o.id));
      const nextSelIds = s.selectedObjectIds.filter((id) => idSet.has(id));
      const nextTable =
        s.selectedTableId && idSet.has(s.selectedTableId)
          ? s.selectedTableId
          : null;
      set({
        objects: next,
        objectHistoryPast: newPast,
        objectHistoryFuture: [currentSnap, ...s.objectHistoryFuture].slice(
          -MAX_OBJECT_HISTORY,
        ),
        pages:
          s.currentPageId != null
            ? s.pages.map((p) =>
                p.id === s.currentPageId ? { ...p, objects: next } : p,
              )
            : s.pages,
        selectedObjectIds: nextSelIds,
        selectedObjectId: nextSelIds[0] ?? null,
        selectedTableId: nextTable,
        selectedCellId: null,
        editingCell: null,
      });
    },
    redoObjects: () => {
      const s = get();
      if (s.objectHistoryFuture.length === 0) return;
      const forward = s.objectHistoryFuture[0]!;
      const newFuture = s.objectHistoryFuture.slice(1);
      const currentSnap = cloneObjectList(s.objects);
      const next = cloneObjectList(forward);
      const idSet = new Set(next.map((o) => o.id));
      const nextSelIds = s.selectedObjectIds.filter((id) => idSet.has(id));
      const nextTable =
        s.selectedTableId && idSet.has(s.selectedTableId)
          ? s.selectedTableId
          : null;
      set({
        objects: next,
        objectHistoryPast: [...s.objectHistoryPast, currentSnap].slice(
          -MAX_OBJECT_HISTORY,
        ),
        objectHistoryFuture: newFuture,
        pages:
          s.currentPageId != null
            ? s.pages.map((p) =>
                p.id === s.currentPageId ? { ...p, objects: next } : p,
              )
            : s.pages,
        selectedObjectIds: nextSelIds,
        selectedObjectId: nextSelIds[0] ?? null,
        selectedTableId: nextTable,
        selectedCellId: null,
        editingCell: null,
      });
    },
    bringObjectForward: (id) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const objs = [...state.objects];
      const i = objs.findIndex((o) => o.id === id);
      if (i === -1 || i >= objs.length - 1) return;
      [objs[i], objs[i + 1]] = [objs[i + 1]!, objs[i]!];
      if (state.currentPageId) {
        set(withSyncedCurrentPageObjects(state, objs));
        return;
      }
      set({ objects: objs });
    },
    sendObjectBackward: (id) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const objs = [...state.objects];
      const i = objs.findIndex((o) => o.id === id);
      if (i <= 0) return;
      [objs[i], objs[i - 1]] = [objs[i - 1]!, objs[i]!];
      if (state.currentPageId) {
        set(withSyncedCurrentPageObjects(state, objs));
        return;
      }
      set({ objects: objs });
    },
    addObject: (object) => {
      const state = get();
      if (state.currentPageId) {
        const currentPage = state.pages.find(
          (p) => p.id === state.currentPageId,
        );
        if (currentPage) {
          set((state) => ({
            objects: [...state.objects, object],
            pages: state.pages.map((p) =>
              p.id === state.currentPageId
                ? { ...p, objects: [...p.objects, object] }
                : p,
            ),
          }));
          return;
        }
      }
      set((state) => ({ objects: [...state.objects, object] }));
    },
    setGuides: (guides) => {
      const state = get();
      if (state.currentPageId) {
        set((state) => ({
          guides,
          pages: state.pages.map((p) =>
            p.id === state.currentPageId ? { ...p, guides } : p,
          ),
        }));
        return;
      }
      set({ guides });
    },
    updateObject: (id, updates) => {
      set((state) => {
        const nextObjects = state.objects.map((obj) =>
          obj.id === id ? { ...obj, ...updates } : obj,
        );
        return withSyncedCurrentPageObjects(state, nextObjects);
      });
    },
    deleteObject: (id, options) => {
      if (!options?.skipHistory) {
        get().commitObjectHistoryBeforeChange();
      }
      const state = get();
      if (state.currentPageId) {
        set((state) => ({
          objects: state.objects.filter((obj) => obj.id !== id),
          pages: state.pages.map((p) =>
            p.id === state.currentPageId
              ? { ...p, objects: p.objects.filter((obj) => obj.id !== id) }
              : p,
          ),
          selectedObjectId:
            state.selectedObjectId === id ? null : state.selectedObjectId,
          selectedTableId:
            state.selectedTableId === id ? null : state.selectedTableId,
        }));
        return;
      }
      set((state) => ({
        objects: state.objects.filter((obj) => obj.id !== id),
        selectedObjectId:
          state.selectedObjectId === id ? null : state.selectedObjectId,
        selectedTableId:
          state.selectedTableId === id ? null : state.selectedTableId,
      }));
    },
    duplicateObject: (id) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const objectToDuplicate = state.objects.find((obj) => obj.id === id);
      if (!objectToDuplicate) return;

      const duplicatedObject = cloneObjectsForPaste([objectToDuplicate])[0];

      if (state.currentPageId) {
        set((state) => ({
          objects: [...state.objects, duplicatedObject],
          pages: state.pages.map((p) =>
            p.id === state.currentPageId
              ? { ...p, objects: [...p.objects, duplicatedObject] }
              : p,
          ),
          selectedObjectId: duplicatedObject.id,
        }));
        return;
      }
      set((state) => ({
        objects: [...state.objects, duplicatedObject],
        selectedObjectId: duplicatedObject.id,
      }));
    },
    copySelectedObjects: () => {
      const state = get();
      const selectedIds =
        state.selectedObjectIds.length > 0
          ? state.selectedObjectIds
          : state.selectedObjectId
          ? [state.selectedObjectId]
          : [];

      if (selectedIds.length === 0) return;

      const selectedIdSet = new Set(selectedIds);
      const groupsToCopy = new Set(
        state.objects
          .filter((obj) => selectedIdSet.has(obj.id) && obj.type === "group")
          .map((obj) => obj.id),
      );
      const objectsToCopy = state.objects.filter(
        (obj) =>
          selectedIdSet.has(obj.id) || groupsToCopy.has(obj.groupId || ""),
      );

      if (objectsToCopy.length === 0) return;
      set({ clipboardObjects: objectsToCopy });
    },
    pasteCopiedObjects: () => {
      const state = get();
      if (state.clipboardObjects.length === 0) return;

      get().commitObjectHistoryBeforeChange();
      const pastedObjects = cloneObjectsForPaste(state.clipboardObjects);
      const pastedIds = pastedObjects.map((obj) => obj.id);

      if (state.currentPageId) {
        set((state) => ({
          objects: [...state.objects, ...pastedObjects],
          pages: state.pages.map((p) =>
            p.id === state.currentPageId
              ? { ...p, objects: [...p.objects, ...pastedObjects] }
              : p,
          ),
          selectedObjectId: pastedIds[0] ?? null,
          selectedObjectIds: pastedIds,
        }));
        return;
      }

      set((state) => ({
        objects: [...state.objects, ...pastedObjects],
        selectedObjectId: pastedIds[0] ?? null,
        selectedObjectIds: pastedIds,
      }));
    },
    reorderObjects: (draggedId, targetId) => {
      if (draggedId === targetId) return;

      get().commitObjectHistoryBeforeChange();
      const state = get();
      if (state.currentPageId) {
        set((state) =>
          withSyncedCurrentPageObjects(
            state,
            reorderByIds(state.objects, draggedId, targetId),
          ),
        );
        return;
      }

      set((state) => ({
        objects: reorderByIds(state.objects, draggedId, targetId),
      }));
    },
    selectedObjectId: null,
    setSelectedObjectId: (id) => {
      set({
        selectedObjectId: id,
        selectedObjectIds: id ? [id] : [],
      });
    },
    selectedObjectIds: [],
    setSelectedObjectIds: (ids) => {
      set({
        selectedObjectIds: ids,
        selectedObjectId: ids.length > 0 ? ids[0] : null,
      });
    },
    addToSelection: (id) => {
      const state = get();
      if (!state.selectedObjectIds.includes(id)) {
        const newIds = [...state.selectedObjectIds, id];
        set({
          selectedObjectIds: newIds,
          selectedObjectId: newIds[0],
        });
      }
    },
    removeFromSelection: (id) => {
      const state = get();
      const newIds = state.selectedObjectIds.filter(
        (selectedId) => selectedId !== id,
      );
      set({
        selectedObjectIds: newIds,
        selectedObjectId: newIds.length > 0 ? newIds[0] : null,
      });
    },
    selectedTableId: null,
    setSelectedTableId: (id) => set({ selectedTableId: id }),
    selectedCellId: null,
    setSelectedCellId: (id) => set({ selectedCellId: id }),
    editingCell: null,
    setEditingCell: (cell) => set({ editingCell: cell }),
    addTable: (x, y, rows = 4, cols = 5) => {
      const tableId = createScopedId("table");
      const cellWidth = 100;
      const cellHeight = 50;
      const tableData: TableData = {
        id: tableId,
        rows,
        cols,
        cellWidth,
        cellHeight,
        cells: createInitialCells(rows, cols, cellWidth, cellHeight),
        stroke: "#808080",
        strokeWidth: 1,
      };
      const tableObject: EditorObject = {
        id: tableId,
        type: "grid",
        x,
        y,
        width: cols * cellWidth,
        height: rows * cellHeight,
        tableData,
      };
      get().addObject(tableObject);
      get().setSelectedObjectId(tableId);
      get().setSelectedTableId(tableId);
    },
    addRow: (tableId) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      const newRow = tableData.rows;
      const newCells = { ...tableData.cells };

      for (let col = 0; col < tableData.cols; col++) {
        const cellId = createTableCellId(newRow, col);
        newCells[cellId] = {
          id: cellId,
          text: "",
          background: "#f5f5f5",
          width: tableData.cellWidth,
          height: tableData.cellHeight,
        };
      }

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          rows: tableData.rows + 1,
          cells: newCells,
        },
        height: (tableData.rows + 1) * tableData.cellHeight,
      });
    },
    addColumn: (tableId) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      const newCol = tableData.cols;
      const newCells = { ...tableData.cells };

      for (let row = 0; row < tableData.rows; row++) {
        const cellId = createTableCellId(row, newCol);
        newCells[cellId] = {
          id: cellId,
          text: "",
          background: "#f5f5f5",
          width: tableData.cellWidth,
          height: tableData.cellHeight,
        };
      }

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          cols: tableData.cols + 1,
          cells: newCells,
        },
        width: (tableData.cols + 1) * tableData.cellWidth,
      });
    },
    removeRow: (tableId) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      if (tableData.rows <= 1) return;

      const newCells = { ...tableData.cells };
      for (let col = 0; col < tableData.cols; col++) {
        const cellId = createTableCellId(tableData.rows - 1, col);
        delete newCells[cellId];
      }

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          rows: tableData.rows - 1,
          cells: newCells,
        },
        height: (tableData.rows - 1) * tableData.cellHeight,
      });
    },
    removeColumn: (tableId) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      if (tableData.cols <= 1) return;

      const newCells = { ...tableData.cells };
      for (let row = 0; row < tableData.rows; row++) {
        const cellId = createTableCellId(row, tableData.cols - 1);
        delete newCells[cellId];
      }

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          cols: tableData.cols - 1,
          cells: newCells,
        },
        width: (tableData.cols - 1) * tableData.cellWidth,
      });
    },
    updateCellValue: (tableId, cellId, text) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      const updatedCells = {
        ...tableData.cells,
        [cellId]: {
          ...tableData.cells[cellId],
          text,
        },
      };

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          cells: updatedCells,
        },
      });
    },
    changeCellBackground: (tableId, cellId, color) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      const updatedCells = {
        ...tableData.cells,
        [cellId]: {
          ...tableData.cells[cellId],
          background: color,
        },
      };

      state.updateObject(tableId, {
        tableData: {
          ...tableData,
          cells: updatedCells,
        },
      });
    },
    applyTableResize: (tableId, newWidth, newHeight) => {
      const state = get();
      const obj = state.objects.find((o) => o.id === tableId);
      if (!obj || !obj.tableData) return;

      const { tableData } = obj;
      const newCellWidth = newWidth / tableData.cols;
      const newCellHeight = newHeight / tableData.rows;

      const updatedCells: Record<string, TableCell> = {};
      for (let row = 0; row < tableData.rows; row++) {
        for (let col = 0; col < tableData.cols; col++) {
          const cellId = createTableCellId(row, col);
          const oldCell = tableData.cells[cellId];
          updatedCells[cellId] = {
            ...oldCell,
            width: newCellWidth,
            height: newCellHeight,
          };
        }
      }

      state.updateObject(tableId, {
        width: newWidth,
        height: newHeight,
        tableData: {
          ...tableData,
          cellWidth: newCellWidth,
          cellHeight: newCellHeight,
          cells: updatedCells,
        },
      });
    },
    stageRef: null,
    setStageRef: (ref) => set({ stageRef: ref }),
    layerRef: null,
    setLayerRef: (ref) => set({ layerRef: ref }),
    zoom: 1,
    setZoom: (zoom) => set({ zoom }),
    zoomIn: () => {
      const state = get();
      const newZoom = Math.min(state.zoom + 0.1, 2);
      set({ zoom: newZoom });
    },
    zoomOut: () => {
      const state = get();
      const newZoom = Math.max(state.zoom - 0.1, 0.5);
      set({ zoom: newZoom });
    },
    pages: [initialPage],
    currentPageId: initialPage.id,
    addPage: (name) => {
      const state = get();
      const pageNumber = state.pages.length + 1;
      const newPage = createInitialPage(name || `${pageNumber}`);
      set((state) => ({
        pages: [...state.pages, newPage],
        currentPageId: newPage.id,
        objects: [],
        guides: [],
        selectedObjectId: null,
        objectHistoryPast: [],
        objectHistoryFuture: [],
      }));
    },
    duplicatePage: (pageId) => {
      const state = get();
      const pageToDuplicate = state.pages.find((p) => p.id === pageId);
      if (!pageToDuplicate) return;

      const duplicatedObjects = pageToDuplicate.objects.map((obj) => ({
        ...obj,
        id: createScopedId(obj.id),
      }));

      const newPage: Page = {
        id: createScopedId("page"),
        name: `${pageToDuplicate.name} (کپی)`,
        objects: duplicatedObjects,
        guides: [...pageToDuplicate.guides],
        backgroundType: pageToDuplicate.backgroundType,
        backgroundColor: pageToDuplicate.backgroundColor,
        backgroundGradient: pageToDuplicate.backgroundGradient,
        backgroundImageUrl: pageToDuplicate.backgroundImageUrl,
        backgroundVideoUrl: pageToDuplicate.backgroundVideoUrl,
      };

      set((state) => ({
        pages: [...state.pages, newPage],
      }));
    },
    deletePage: (pageId) => {
      const state = get();
      if (state.pages.length <= 1) return;

      const newPages = state.pages.filter((p) => p.id !== pageId);
      const newCurrentPageId =
        state.currentPageId === pageId
          ? newPages[0]?.id || null
          : state.currentPageId;

      const newCurrentPage = newPages.find((p) => p.id === newCurrentPageId);

      set({
        pages: applyDefaultPageNames(newPages),
        currentPageId: newCurrentPageId,
        objects: newCurrentPage?.objects || [],
        guides: newCurrentPage?.guides || [],
        selectedObjectId: null,
        objectHistoryPast: [],
        objectHistoryFuture: [],
      });
    },
    reorderPages: (draggedId, targetId) => {
      if (draggedId === targetId) return;

      set((state) => {
        const pagesWithCurrentObjects = state.currentPageId
          ? state.pages.map((p) =>
              p.id === state.currentPageId
                ? { ...p, objects: state.objects, guides: state.guides }
                : p,
            )
          : state.pages;

        return {
          pages: applyDefaultPageNames(
            reorderByIds(pagesWithCurrentObjects, draggedId, targetId),
          ),
        };
      });
    },
    setCurrentPage: (pageId) => {
      const state = get();
      const targetPage = state.pages.find((p) => p.id === pageId);
      if (!targetPage) return;

      // Save current objects to current page before switching
      const updatedPages = state.currentPageId
        ? state.pages.map((p) =>
            p.id === state.currentPageId
              ? { ...p, objects: state.objects, guides: state.guides }
              : p,
          )
        : state.pages;

      const targetPageFromUpdated = updatedPages.find((p) => p.id === pageId);
      if (!targetPageFromUpdated) return;

      set({
        pages: updatedPages,
        currentPageId: pageId,
        objects: targetPageFromUpdated.objects,
        guides: targetPageFromUpdated.guides || [],
        selectedObjectId: null,
        objectHistoryPast: [],
        objectHistoryFuture: [],
      });
    },
    updatePageName: (pageId, name) => {
      set((state) => ({
        pages: state.pages.map((p) => (p.id === pageId ? { ...p, name } : p)),
      }));
    },
    toggleObjectVisibility: (id) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const obj = state.objects.find((o) => o.id === id);
      if (!obj) return;

      const newVisible = !(obj.visible ?? true);
      const shouldDeselect =
        !newVisible &&
        (state.selectedObjectId === id || state.selectedTableId === id);

      if (state.currentPageId) {
        set((state) => ({
          objects: state.objects.map((obj) =>
            obj.id === id ? { ...obj, visible: newVisible } : obj,
          ),
          pages: state.pages.map((p) =>
            p.id === state.currentPageId
              ? {
                  ...p,
                  objects: p.objects.map((obj) =>
                    obj.id === id ? { ...obj, visible: newVisible } : obj,
                  ),
                }
              : p,
          ),
          selectedObjectId: shouldDeselect ? null : state.selectedObjectId,
          selectedTableId: shouldDeselect ? null : state.selectedTableId,
        }));
        return;
      }
      set((state) => ({
        objects: state.objects.map((obj) =>
          obj.id === id ? { ...obj, visible: newVisible } : obj,
        ),
        selectedObjectId: shouldDeselect ? null : state.selectedObjectId,
        selectedTableId: shouldDeselect ? null : state.selectedTableId,
      }));
    },
    groupObjects: (objectIds) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();
      const result = buildGroupResult(state.objects, objectIds);
      if (!result) return;

      if (state.currentPageId) {
        set((state) => ({
          ...withSyncedCurrentPageObjects(state, result.nextObjects),
          selectedObjectId: result.groupId,
          selectedObjectIds: [result.groupId],
        }));
        return;
      }
      set(() => ({
        objects: result.nextObjects,
        selectedObjectId: result.groupId,
        selectedObjectIds: [result.groupId],
      }));
    },
    ungroupObjects: (groupId) => {
      get().commitObjectHistoryBeforeChange();
      const state = get();

      if (state.currentPageId) {
        set((state) => ({
          objects: ungroupById(state.objects, groupId),
          pages: state.pages.map((p) =>
            p.id === state.currentPageId
              ? { ...p, objects: ungroupById(p.objects, groupId) }
              : p,
          ),
          selectedObjectId: null,
          selectedObjectIds: [],
        }));
        return;
      }
      set((state) => ({
        objects: ungroupById(state.objects, groupId),
        selectedObjectId: null,
        selectedObjectIds: [],
      }));
    },
    getGroupObjects: (groupId: string) => {
      const state = get();
      return state.objects.filter((obj) => obj.groupId === groupId);
    },
    resetEditor: () => {
      const page = createInitialPage("جلد");
      set({
        pages: [page],
        currentPageId: page.id,
        objects: page.objects,
        guides: [],
        selectedObjectId: null,
        selectedObjectIds: [],
        objectHistoryPast: [],
        objectHistoryFuture: [],
        documentSettings: { ...DEFAULT_DOCUMENT_SETTINGS },
        gallery: [],
      });
    },
    loadPages: (pages, documentSettings, gallery) => {
      if (pages.length === 0) return;
      const fallbackSettings = documentSettings ?? {};
      const normalizedPages = pages.map((page) => ({
        ...page,
        id: isInvalidPageId(page.id) ? createScopedId("page") : page.id,
        guides: page.guides || [],
        objects: normalizePageObjects(page.objects || []),
        backgroundType:
          page.backgroundType ?? fallbackSettings.backgroundType ?? "color",
        backgroundColor:
          page.backgroundColor ?? fallbackSettings.backgroundColor ?? "#ffffff",
        backgroundGradient: page.backgroundGradient ??
          fallbackSettings.backgroundGradient ?? {
            from: "#ffffff",
            to: "#e2e8f0",
            angle: 135,
          },
        backgroundImageUrl:
          page.backgroundImageUrl ?? fallbackSettings.backgroundImageUrl ?? "",
        backgroundVideoUrl:
          page.backgroundVideoUrl ?? fallbackSettings.backgroundVideoUrl ?? "",
      }));
      const firstPage = normalizedPages[0];
      const currentSettings = get().documentSettings;
      set({
        pages: normalizedPages,
        currentPageId: firstPage.id,
        objects: firstPage.objects,
        guides: firstPage.guides || [],
        selectedObjectId: null,
        selectedObjectIds: [],
        objectHistoryPast: [],
        objectHistoryFuture: [],
        ...(documentSettings && {
          documentSettings: { ...currentSettings, ...documentSettings },
        }),
        ...(gallery !== undefined && { gallery }),
      });
    },
  };
});
