// src/pages/Dashboard.jsx
import React, { useCallback, useEffect, useMemo, useState } from "react";
import Filter from "../components/Filter";
import { Axiosinstance } from "../services/auth/Axiosinstance";
import { useToast } from "../components/ToastProvider";

import { IoGrid, IoList } from "react-icons/io5";

import {
  TYPE_EXTENSION_MAP,
  flattenFolderTree,
  getFileExtension,
  getFileId,
  getFolderId,
  joinUrl,
} from "../utils/storage";

import { useStorageColors } from "../hooks/useStorageColors";
import { useStorageDashboardItems } from "../hooks/useStorageDashboardItems";

import SearchCategoryBar from "../components/storage/SearchCategoryBar";
import SuggestedFolders from "../components/storage/SuggestedFolders";
import RenameModal from "../components/storage/RenameModal";
import MoveModal from "../components/storage/MoveModal";
import FilesList from "../components/storage/FilesList";
import FilesGrid from "../components/storage/FilesGrid";
import ColorPickerModal from "../components/storage/ColorPickerModal";

import ZipModal from "../components/storage/ZipModal";
import ShareModal from "../components/storage/ShareModal";
import DetailsModal from "../components/storage/DetailsModal";

import { FileMenu, FolderMenu } from "../components/storage/ItemMenus";
import PaginationControls from "../components/storage/PaginationControls"; // ✅ جدید

/* ---------------- session helpers ---------------- */
const SESSION_PAGE_SIZE_KEY = "storage.pageSize";

const readSessionInt = (key, fallback) => {
  try {
    const raw = sessionStorage.getItem(key);
    const n = Number.parseInt(raw, 10);
    return Number.isFinite(n) ? n : fallback;
  } catch {
    return fallback;
  }
};

const clampInt = (v, min, max) => {
  const n = Number.parseInt(String(v || ""), 10);
  if (!Number.isFinite(n)) return min;
  return Math.max(min, Math.min(max, n));
};

export default function Dashboard() {
  const toastApi = useToast();

  // ✅ wrapper برای سازگاری ToastProvider‌های مختلف
  const toast = useMemo(() => {
    if (toastApi?.success && toastApi?.error) return toastApi;
    if (toastApi?.show) {
      return {
        success: (m) => toastApi.show({ type: "success", message: m }),
        error: (m) => toastApi.show({ type: "error", message: m }),
        info: (m) => toastApi.show({ type: "info", message: m }),
      };
    }
    if (toastApi?.addToast) {
      return {
        success: (m) => toastApi.addToast("success", m),
        error: (m) => toastApi.addToast("error", m),
        info: (m) => toastApi.addToast("info", m),
      };
    }
    return { success: () => {}, error: () => {}, info: () => {} };
  }, [toastApi]);

  const baseURL = Axiosinstance.defaults.baseURL;

  // data hooks
  const {
    folders,
    foldersTreeRaw,
    files,
    loading,
    loadError,
    ownerEmails,
    requestRefresh,
  } = useStorageDashboardItems({ Axiosinstance, toast });

  const { colorsCfg, colorOptions, colorsLoading } = useStorageColors({
    Axiosinstance,
    toast,
  });

  // filters ui states
  const [filterChanges, setFilterChanges] = useState(null);
  const [filterSpace, setFilterSpace] = useState(null);
  const [filterOwner, setFilterOwner] = useState(null);

  const [viewMode, setViewMode] = useState("grid");
  const [searchTerm, setSearchTerm] = useState("");
  const [selectedType, setSelectedType] = useState(null);

  // portal menus
  const [fileMenu, setFileMenu] = useState({ id: null, rect: null });
  const [folderMenu, setFolderMenu] = useState({ id: null, rect: null });

  const closeFileMenu = () => setFileMenu({ id: null, rect: null });
  const closeFolderMenu = () => setFolderMenu({ id: null, rect: null });

  const toggleFileMenu = (id, e) => {
    e.preventDefault();
    e.stopPropagation();
    const rect = e.currentTarget.getBoundingClientRect();
    setFileMenu((prev) =>
      String(prev.id) === String(id) ? { id: null, rect: null } : { id, rect }
    );
    setFolderMenu({ id: null, rect: null });
  };

  const toggleFolderMenu = (id, e) => {
    e.preventDefault();
    e.stopPropagation();
    const rect = e.currentTarget.getBoundingClientRect();
    setFolderMenu((prev) =>
      String(prev.id) === String(id) ? { id: null, rect: null } : { id, rect }
    );
    setFileMenu({ id: null, rect: null });
  };

  // modals state
  const [renameModal, setRenameModal] = useState({
    open: false,
    type: null,
    item: null,
    value: "",
  });
  const [moveModal, setMoveModal] = useState({
    open: false,
    item: null,
    target: "root",
    createName: "",
    creating: false,
    moving: false,
  });
  const [detailsModal, setDetailsModal] = useState({
    open: false,
    type: null,
    item: null,
  });
  const [shareModal, setShareModal] = useState({
    open: false,
    file: null,
    data: null,
    downloadLink: "",
    copying: false,
    revoking: false,
    creating: false,
  });
  const [colorModal, setColorModal] = useState({
    open: false,
    type: null,
    item: null,
  });
  const [zipModal, setZipModal] = useState({
    open: false,
    items: [],
    name: "",
    folder_id: "root",
    picking: false,
    pickValue: "",
    zipping: false,
  });

  const openDetails = (type, item) => {
    closeFileMenu();
    closeFolderMenu();
    setDetailsModal({ open: true, type, item });
  };

  // clipboard
  const copyToClipboard = async (text) => {
    try {
      if (navigator?.clipboard?.writeText) {
        await navigator.clipboard.writeText(text);
        return true;
      }
    } catch {}
    try {
      const ta = document.createElement("textarea");
      ta.value = text;
      ta.style.position = "fixed";
      ta.style.left = "-9999px";
      document.body.appendChild(ta);
      ta.focus();
      ta.select();
      const ok = document.execCommand("copy");
      document.body.removeChild(ta);
      return ok;
    } catch {
      return false;
    }
  };

  /* -------------------- filtering (همون منطق خودت) -------------------- */
  const filteredFiles = useMemo(() => {
    return (files || []).filter((file) => {
      const name = file.name || "";
      const lowerName = name.toLowerCase();
      const search = searchTerm.toLowerCase();

      if (search && !lowerName.includes(search)) return false;

      if (selectedType && selectedType !== "پوشه‌ها") {
        const allowedExts = TYPE_EXTENSION_MAP[selectedType];
        if (allowedExts && allowedExts.length > 0) {
          const ext = getFileExtension(name);
          if (!allowedExts.includes(ext)) return false;
        }
      }

      if (filterChanges && file.updated_at) {
        const updated = new Date(file.updated_at);
        const now = new Date();
        let fromDate = null;

        switch (filterChanges) {
          case "today":
            fromDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
            break;
          case "7days":
            fromDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
            break;
          case "30days":
            fromDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
            break;
          case "thisYear":
            fromDate = new Date(now.getFullYear(), 0, 1);
            break;
          case "lastYear":
            fromDate = new Date(now.getFullYear() - 1, 0, 1);
            break;
          default:
            break;
        }

        if (fromDate && updated < fromDate) return false;
      }

      if (filterSpace) {
        if (filterSpace === "starred" && !file.is_starred) return false;
        if (filterSpace === "trash" && !file.in_trash) return false;
      }

      if (filterOwner && file.owner_email && file.owner_email !== filterOwner)
        return false;

      return true;
    });
  }, [files, searchTerm, selectedType, filterChanges, filterSpace, filterOwner]);

  const filteredFolders = useMemo(() => {
    const s = searchTerm.toLowerCase();
    return (folders || []).filter((folder) =>
      (folder.name || "").toLowerCase().includes(s)
    );
  }, [folders, searchTerm]);

  const allFoldersFlat = useMemo(
    () => flattenFolderTree(foldersTreeRaw),
    [foldersTreeRaw]
  );

  /* -------------------- ✅ Pagination States -------------------- */
  // pageSize shared across whole site (session)
  const [pageSize, setPageSize] = useState(() =>
    clampInt(readSessionInt(SESSION_PAGE_SIZE_KEY, 8), 1, 200)
  );

  // files/folders each has its own page index
  const [filesPage, setFilesPage] = useState(1);
  const [foldersPage, setFoldersPage] = useState(1);

  // وقتی فیلتر/جستجو تغییر می‌کنه برگرد صفحه 1
  useEffect(() => {
    setFilesPage(1);
    setFoldersPage(1);
  }, [searchTerm, selectedType, filterChanges, filterSpace, filterOwner, viewMode, pageSize]);

  const paginate = (arr, page, size) => {
    const total = Array.isArray(arr) ? arr.length : 0;
    const totalPages = Math.max(1, Math.ceil(total / size));
    const p = clampInt(page, 1, totalPages);
    const start = (p - 1) * size;
    const end = start + size;
    return {
      total,
      totalPages,
      page: p,
      items: (arr || []).slice(start, end),
    };
  };

  const pagedFolders = useMemo(
    () => paginate(filteredFolders, foldersPage, pageSize),
    [filteredFolders, foldersPage, pageSize]
  );

  const pagedFiles = useMemo(
    () => paginate(filteredFiles, filesPage, pageSize),
    [filteredFiles, filesPage, pageSize]
  );

  /* -------------------- actions (همون endpointها) -------------------- */
  const getSharedDownloadUrlByToken = (token) =>
    joinUrl(baseURL, `storage/share/${token}/download/`);

  const openRename = (type, item) => {
    closeFileMenu();
    closeFolderMenu();
    setRenameModal({ open: true, type, item, value: item?.name || "" });
  };

  const handleRenameFile = async (file, name) => {
    try {
      const id = getFileId(file);
      await Axiosinstance.patch(`storage/files/${id}/rename/`, { name });
      toast.success("نام فایل تغییر کرد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("تغییر نام فایل ناموفق بود.");
    }
  };

  const handleRenameFolder = async (folder, name) => {
    try {
      const id = getFolderId(folder);
      await Axiosinstance.patch(`storage/folders/${id}/`, { name });
      toast.success("نام پوشه تغییر کرد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("تغییر نام پوشه ناموفق بود.");
    }
  };

  const openMove = (file) => {
    closeFileMenu();
    closeFolderMenu();
    setMoveModal({
      open: true,
      item: file,
      target: "root",
      createName: "",
      creating: false,
      moving: false,
    });
  };

  const handleMoveFile = async (file, destination_folder_id) => {
    try {
      const id = getFileId(file);
      await Axiosinstance.post(`storage/files/${id}/move/`, {
        destination_folder_id,
      });
      toast.success("فایل منتقل شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("انتقال فایل ناموفق بود.");
    }
  };

  const handleCreateFolder = async (name, parent) => {
    try {
      const payload = parent === "root" ? { name } : { name, parent };
      await Axiosinstance.post(`storage/folders/`, payload);
      toast.success("پوشه ساخته شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("ساخت پوشه ناموفق بود.");
    }
  };

  const handleSetFileColor = async (file, color_id) => {
    const id = getFileId(file);
    if (!id) return toast.error("شناسه فایل پیدا نشد.");
    try {
      await Axiosinstance.patch(`storage/files/${id}/color/`, {
        color_id: color_id ?? null,
      });
      toast.success("رنگ فایل تغییر کرد ✅");
    } catch {
      toast.error("تغییر رنگ فایل ناموفق بود.");
    }
  };

  const handleSetFolderColor = async (folder, color_id) => {
    const id = getFolderId(folder);
    if (!id) return toast.error("شناسه پوشه پیدا نشد.");
    try {
      await Axiosinstance.patch(`storage/folders/${id}/color/`, {
        color_id: color_id ?? null,
      });
      toast.success("رنگ پوشه تغییر کرد ✅");
    } catch {
      toast.error("تغییر رنگ پوشه ناموفق بود.");
    }
  };

  const handleDownloadFile = async (file) => {
    try {
      const id = getFileId(file);
      const response = await Axiosinstance.get(`storage/files/${id}/download/`, {
        responseType: "blob",
      });
      const blobUrl = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement("a");
      link.href = blobUrl;
      link.download = file.name || "file";
      document.body.appendChild(link);
      link.click();
      link.remove();
      window.URL.revokeObjectURL(blobUrl);
      toast.success("دانلود شروع شد ✅");
    } catch {
      toast.error("دانلود فایل ناموفق بود.");
    }
  };

  const handleShareFile = async (file) => {
    try {
      setShareModal({
        open: true,
        file,
        data: null,
        downloadLink: "",
        copying: false,
        revoking: false,
        creating: false,
      });
    } catch {
      toast.error("باز کردن اشتراک‌گذاری ناموفق بود.");
    }
  };

  const handleRevokeShare = async () => {
    const token = shareModal?.data?.token;
    const file = shareModal?.file;
    if (!token || !file) return toast.error("توکن اشتراک پیدا نشد.");
    try {
      setShareModal((p) => ({ ...p, revoking: true }));
      const id = getFileId(file);
      await Axiosinstance.post(`storage/files/${id}/share/revoke/`, { token });
      toast.success("اشتراک‌گذاری لغو شد ✅");
      setShareModal({
        open: false,
        file: null,
        data: null,
        downloadLink: "",
        copying: false,
        revoking: false,
        creating: false,
      });
    } catch {
      toast.error("لغو اشتراک ناموفق بود.");
      setShareModal((p) => ({ ...p, revoking: false }));
    }
  };

  const handleTrashFile = async (file) => {
    try {
      const id = getFileId(file);
      await Axiosinstance.post(`storage/files/${id}/trash/`);
      toast.success("به سطل زباله منتقل شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("انتقال به سطل زباله ناموفق بود.");
    }
  };

  const handleRestoreFile = async (file) => {
    try {
      const id = getFileId(file);
      await Axiosinstance.post(`storage/files/${id}/restore/`);
      toast.success("فایل بازیابی شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("بازگردانی فایل ناموفق بود.");
    }
  };

  const handleHardDeleteFile = async (file) => {
    if (!window.confirm("حذف دائمی این فایل قابل بازگشت نیست. آیا مطمئن هستید؟"))
      return;
    try {
      const id = getFileId(file);
      await Axiosinstance.delete(`storage/files/${id}/hard-delete/`);
      toast.success("حذف دائمی انجام شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("حذف دائمی فایل ناموفق بود.");
    }
  };

  const handleStarFile = async (file) => {
    try {
      const id = getFileId(file);
      const res = await Axiosinstance.post(`storage/files/${id}/star/`);
      const is_starred =
        typeof res.data?.is_starred === "boolean"
          ? res.data.is_starred
          : !file.is_starred;
      toast.success(
        is_starred ? "به ستاره‌دارها اضافه شد ⭐" : "از ستاره‌دارها حذف شد ✅"
      );
      requestRefresh({ silent: true });
    } catch {
      toast.error("عملیات ستاره‌دار کردن ناموفق بود.");
    }
  };

  const handleTrashFolder = async (folder) => {
    try {
      const id = getFolderId(folder);
      await Axiosinstance.post(`storage/folders/${id}/trash/`);
      toast.success("پوشه به سطل زباله منتقل شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("انتقال پوشه ناموفق بود.");
    }
  };

  const handleRestoreFolder = async (folder) => {
    try {
      const id = getFolderId(folder);
      await Axiosinstance.post(`storage/folders/${id}/restore/`);
      toast.success("پوشه بازیابی شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("بازگردانی پوشه ناموفق بود.");
    }
  };

  const handleHardDeleteFolder = async (folder) => {
    if (
      !window.confirm("حذف دائمی این پوشه قابل بازگشت نیست. آیا مطمئن هستید؟")
    )
      return;
    try {
      const id = getFolderId(folder);
      await Axiosinstance.delete(`storage/folders/${id}/hard-delete/`);
      toast.success("پوشه حذف دائمی شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("حذف دائمی پوشه ناموفق بود.");
    }
  };

  const handleStarFolder = async (folder) => {
    try {
      const id = getFolderId(folder);
      const res = await Axiosinstance.post(`storage/folders/${id}/star/`);
      const is_starred =
        typeof res.data?.is_starred === "boolean"
          ? res.data.is_starred
          : !folder.is_starred;
      toast.success(is_starred ? "پوشه ستاره‌دار شد ⭐" : "از ستاره‌دارها حذف شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("ستاره‌دار کردن پوشه ناموفق بود.");
    }
  };

  // Zip
  const openZipModalWith = (seed) => {
    closeFileMenu();
    closeFolderMenu();
    setZipModal((p) => {
      const exists = (p.items || []).some(
        (it) => it.type === seed.type && String(it.id) === String(seed.id)
      );
      const items = exists ? p.items : [seed];
      const suggestedName =
        seed?.type === "folder"
          ? `${String(seed?.name || "archive").replaceAll("/", "-")}.zip`
          : `${String(seed?.name || "archive").replaceAll("/", "-")}.zip`;

      return {
        open: true,
        items,
        name: p.open ? p.name : suggestedName,
        folder_id: p.folder_id || "root",
        picking: false,
        pickValue: "",
        zipping: false,
      };
    });
  };

  const handleCreateArchive = async () => {
    const name = String(zipModal.name || "").trim();
    if (!name) return toast.error("نام فایل زیپ را وارد کنید.");

    const items = Array.isArray(zipModal.items) ? zipModal.items : [];
    const file_ids = items
      .filter((x) => x.type === "file")
      .map((x) => x.id)
      .filter(Boolean);
    const folder_ids = items
      .filter((x) => x.type === "folder")
      .map((x) => x.id)
      .filter(Boolean);

    if (file_ids.length === 0 && folder_ids.length === 0)
      return toast.error("حداقل یک فایل یا پوشه انتخاب کنید.");

    const folder_id =
      zipModal.folder_id && zipModal.folder_id !== "root"
        ? zipModal.folder_id
        : null;

    try {
      setZipModal((p) => ({ ...p, zipping: true }));
      toast.info("در حال ساخت آرشیو...");

      await Axiosinstance.post("storage/archive/create/", {
        name,
        folder_id,
        file_ids,
        folder_ids,
      });

      toast.success("زیپ ساخته شد ✅");
      setZipModal({
        open: false,
        items: [],
        name: "",
        folder_id: "root",
        picking: false,
        pickValue: "",
        zipping: false,
      });
      requestRefresh({ silent: true });
    } catch {
      toast.error("زیپ کردن ناموفق بود.");
      setZipModal((p) => ({ ...p, zipping: false }));
    }
  };

  const handleExtractZip = async (file, opts = {}) => {
    const id = getFileId(file);
    if (!id) return toast.error("شناسه فایل پیدا نشد.");

    const payload = {
      zip_file_id: id,
      target_folder_id: opts?.target_folder_id ?? null,
      create_subfolder: opts?.create_subfolder ?? true,
      overwrite: opts?.overwrite ?? false,
    };

    try {
      toast.info("در حال Extract ...");
      await Axiosinstance.post("storage/archive/extract/", payload);
      toast.success("انزیپ انجام شد ✅");
      requestRefresh({ silent: true });
    } catch {
      toast.error("انزیپ ناموفق بود.");
    }
  };

  const handleCreateShare = async ({ permission, duration, file }) => {
    if (!file) return toast.error("فایل انتخاب نشده.");
    try {
      setShareModal((p) => ({ ...p, creating: true }));

      const id = getFileId(file);
      const res = await Axiosinstance.post(`storage/files/${id}/share/`, {
        permission,
        duration,
      });

      const downloadLink =
        res.data?.download_url || res.data?.share_url || "";

      setShareModal((p) => ({
        ...p,
        data: res.data,
        downloadLink,
        creating: false,
      }));

      toast.success("لینک اشتراک ساخته شد ✅");
    } catch {
      toast.error("ساخت لینک اشتراک ناموفق بود.");
      setShareModal((p) => ({ ...p, creating: false }));
    }
  };

  /* -------------------- render menus as callbacks -------------------- */
  const renderFileMenu = useCallback(
    (file) => (
      <FileMenu
        file={file}
        colorsCfg={colorsCfg}
        colorOptions={colorOptions}
        colorsLoading={colorsLoading}
        onDetails={(t, it) => {
          openDetails(t, it);
          closeFileMenu();
        }}
        onRename={(f) => openRename("file", f)}
        onZip={(seed) => {
          openZipModalWith(seed);
          closeFileMenu();
        }}
        onOpenColorModal={(f) => {
          closeFileMenu();
          setColorModal({ open: true, type: "file", item: f });
        }}
        onUnzip={async (f) => {
          await handleExtractZip(f);
          closeFileMenu();
        }}
        onShare={async (f) => {
          await handleShareFile(f);
          closeFileMenu();
        }}
        onDownload={async (f) => {
          await handleDownloadFile(f);
          closeFileMenu();
        }}
        onMove={(f) => openMove(f)}
        onToggleStar={async (f) => {
          await handleStarFile(f);
          closeFileMenu();
        }}
        onTrashOrRestore={async (f, mode) => {
          mode === "trash" ? await handleTrashFile(f) : await handleRestoreFile(f);
          closeFileMenu();
        }}
        onHardDelete={async (f) => {
          await handleHardDeleteFile(f);
          closeFileMenu();
        }}
      />
    ),
    [colorsCfg, colorOptions, colorsLoading, requestRefresh]
  );

  const renderFolderMenu = useCallback(
    (folder) => (
      <FolderMenu
        folder={folder}
        colorsCfg={colorsCfg}
        colorOptions={colorOptions}
        colorsLoading={colorsLoading}
        onDetails={(t, it) => {
          openDetails(t, it);
          closeFolderMenu();
        }}
        onRename={(f) => openRename("folder", f)}
        onZip={(seed) => {
          openZipModalWith(seed);
          closeFolderMenu();
        }}
        onOpenColorModal={(fo) => {
          closeFolderMenu();
          setColorModal({ open: true, type: "folder", item: fo });
        }}
        onToggleStar={async (f) => {
          await handleStarFolder(f);
          closeFolderMenu();
        }}
        onTrashOrRestore={async (f, mode) => {
          mode === "trash" ? await handleTrashFolder(f) : await handleRestoreFolder(f);
          closeFolderMenu();
        }}
        onHardDelete={async (f) => {
          await handleHardDeleteFolder(f);
          closeFolderMenu();
        }}
      />
    ),
    [colorsCfg, colorOptions, colorsLoading, requestRefresh]
  );

  return (
    <div
      className="w-full min-w-0 dark:text-white text-black"
      onClick={() => {
        closeFileMenu();
        closeFolderMenu();
      }}
    >
      {/* Zip */}
      <ZipModal
        state={zipModal}
        setState={setZipModal}
        files={(files || []).map((f) => ({ ...f, id: String(getFileId(f)) }))}
        allFoldersFlat={allFoldersFlat}
        onCreateArchive={handleCreateArchive}
      />

      {/* Details */}
      <DetailsModal
        state={detailsModal}
        onClose={() => setDetailsModal({ open: false, type: null, item: null })}
      />

      {/* Share */}
      <ShareModal
        state={shareModal}
        onClose={() =>
          setShareModal({
            open: false,
            file: null,
            data: null,
            downloadLink: "",
            copying: false,
            revoking: false,
            creating: false,
          })
        }
        onCopy={async (link) => {
          const text =
            link ||
            shareModal?.data?.download_url ||
            shareModal?.data?.share_url ||
            shareModal?.downloadLink ||
            "";
          if (!text) return toast.error("لینکی برای کپی وجود ندارد.");

          setShareModal((p) => ({ ...p, copying: true }));
          const ok = await copyToClipboard(text);
          setShareModal((p) => ({ ...p, copying: false }));
          ok ? toast.success("لینک کپی شد ✅") : toast.error("کپی انجام نشد.");
        }}
        onRevoke={handleRevokeShare}
        onCreate={handleCreateShare}
      />

      <ColorPickerModal
        open={colorModal.open}
        onClose={() => setColorModal({ open: false, type: null, item: null })}
        title="تغییر رنگ"
        itemName={colorModal?.item?.name || ""}
        colorsCfg={colorsCfg}
        colorOptions={colorOptions}
        colorsLoading={colorsLoading}
        onPick={async (colorIdOrNull) => {
          const it = colorModal.item;
          if (!it) return;

          if (colorModal.type === "file") {
            await handleSetFileColor(it, colorIdOrNull);
          } else if (colorModal.type === "folder") {
            await handleSetFolderColor(it, colorIdOrNull);
          }

          requestRefresh({ silent: true });
          setColorModal({ open: false, type: null, item: null });
        }}
      />

      <RenameModal
        state={renameModal}
        setState={setRenameModal}
        onSave={async () => {
          const item = renameModal.item;
          const name = String(renameModal.value || "").trim();
          if (!item || !name) return toast.error("نام جدید را وارد کنید.");

          if (renameModal.type === "file") await handleRenameFile(item, name);
          if (renameModal.type === "folder") await handleRenameFolder(item, name);

          setRenameModal({ open: false, type: null, item: null, value: "" });
        }}
      />

      <MoveModal
        state={moveModal}
        setState={setMoveModal}
        foldersFlat={allFoldersFlat}
        onMove={handleMoveFile}
        onCreateFolder={handleCreateFolder}
      />

      {/* ===== Top bar ===== */}
      <div className="mb-5 rounded-2xl border border-black/10 dark:border-white/10 bg-[#A1E3F910] shadow dark:bg-[#A1E3F910] p-3 sm:p-4">
        <div className="flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between min-w-0">
          <div className="min-w-0">
            <h1 className="text-lg sm:text-xl font-extrabold">داشبورد فایل‌ها</h1>
            <p className="text-xs sm:text-sm text-black/60 dark:text-white/70 truncate">
              سریع جستجو کن، فیلتر بزن، و فایل‌ها رو مدیریت کن
            </p>
          </div>

          <div className="flex items-center gap-2 w-full lg:w-auto">
            <button
              onClick={() => setViewMode("grid")}
              className={`flex-1 lg:flex-none border rounded-xl px-3 py-2 inline-flex items-center justify-center gap-2 transition
                ${
                  viewMode === "grid"
                    ? "dark:bg-[#A1E3F919] bg-[#A1E3F980] dark:text-[#43BEFF] shadow text-black border-[#A1E3F919]"
                    : "border-[#A1E3F919] hover:bg-black/5 dark:text-white text-[#43BEFF] dark:hover:bg-white/10"
                }`}
              title="نمای گرید"
            >
              <IoGrid className="h-5 w-5" />
              <span className="text-sm">گرید</span>
            </button>

            <button
              onClick={() => setViewMode("list")}
              className={`flex-1 lg:flex-none border rounded-xl px-3 py-2 inline-flex items-center justify-center gap-2 transition
                ${
                  viewMode === "list"
                    ? "dark:bg-[#A1E3F919] bg-[#A1E3F980] dark:text-[#43BEFF] shadow text-black border-[#A1E3F919]"
                    : "border-[#A1E3F919] hover:bg-black/5 dark:text-white text-[#43BEFF] dark:hover:bg-white/10"
                }`}
              title="نمای لیست"
            >
              <IoList className="h-5 w-5" />
              <span className="text-sm">لیست</span>
            </button>
          </div>
        </div>
      </div>

      {/* Filters */}
      <div className="flex justify-evenly">
        <Filter
          selectedType={selectedType}
          onTypeChange={(label) => setSelectedType(label)}
          selectedChanges={filterChanges}
          onChangesChange={(val) => setFilterChanges(val)}
          selectedSpace={filterSpace}
          onSpaceChange={(val) => setFilterSpace(val)}
          selectedOwnerEmail={filterOwner}
          onOwnerChange={(email) => setFilterOwner(email)}
          owners={ownerEmails}
        />
      </div>

      {/* Search + Categories */}
      <SearchCategoryBar
        searchTerm={searchTerm}
        onSearchTermChange={setSearchTerm}
        selectedType={selectedType}
        onSelectedTypeChange={setSelectedType}
      />

      {/* ✅ Suggested folders + Pagination */}
      <SuggestedFolders
        folders={pagedFolders.items}
        folderMenu={folderMenu}
        onToggleFolderMenu={toggleFolderMenu}
        onCloseFolderMenu={closeFolderMenu}
        renderFolderMenu={renderFolderMenu}
      />

      <div className="mt-3">
        <PaginationControls
          total={pagedFolders.total}
          page={pagedFolders.page}
          onPageChange={setFoldersPage}
          pageSize={pageSize}
          onPageSizeChange={setPageSize} // ✅ shared across site
          sessionKey={SESSION_PAGE_SIZE_KEY}
          defaultPageSize={8}
          label="پوشه‌ها"
        />
      </div>

      {/* files header */}
      <div className="flex items-center justify-between mb-3 mt-6">
        <button className="dark:text-white text-black font-extrabold text-base sm:text-lg">
          فایل‌های پیشنهادی
        </button>

        <div className="text-xs flex items-center gap-2">
          {loading && <span className="dark:text-white/70">در حال دریافت...</span>}
          {loadError && <span className="text-red-400">{loadError}</span>}
        </div>
      </div>

      {!loading && pagedFiles.total === 0 && (
        <div className="p-4 rounded-2xl bg-white/60 dark:bg-[#16325b] border border-black/10 dark:border-white/10 text-sm text-black/60 dark:text-white/70">
          فایلی برای نمایش وجود ندارد.
        </div>
      )}

      {viewMode === "list" && (
        <FilesList
          files={pagedFiles.items}
          fileMenu={fileMenu}
          onToggleFileMenu={toggleFileMenu}
          onCloseFileMenu={closeFileMenu}
          renderFileMenu={renderFileMenu}
        />
      )}

      {viewMode === "grid" && (
        <FilesGrid
          files={pagedFiles.items}
          baseURL={baseURL}
          Axiosinstance={Axiosinstance}
          fileMenu={fileMenu}
          onToggleFileMenu={toggleFileMenu}
          onCloseFileMenu={closeFileMenu}
          renderFileMenu={renderFileMenu}
        />
      )}

      <div className="mt-3">
        <PaginationControls
          total={pagedFiles.total}
          page={pagedFiles.page}
          onPageChange={setFilesPage}
          pageSize={pageSize}
          onPageSizeChange={setPageSize} // ✅ shared across site
          sessionKey={SESSION_PAGE_SIZE_KEY}
          defaultPageSize={8}
          label="فایل‌ها"
        />
      </div>
    </div>
  );
}
