// Storage screen — Drive-style hierarchical files+folders backed by /api/storage.
// Visibility is per-item: private (only owner) or shared (anyone in the org).
const MAX_FILE_BYTES = 50 * 1024 * 1024;

function formatSize(bytes) {
  if (bytes == null) return "";
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}

function formatDate(iso, lang) {
  if (!iso) return "";
  const d = new Date(iso);
  return d.toLocaleDateString(lang === "es" ? "es-ES" : "en-US", { day: "numeric", month: "short", year: "numeric" });
}

// Extension → icon name. Anything not listed falls back to FileText. Hidden
// files (".gitignore") and extensionless files ("Dockerfile") also default.
const FILE_EXT_ICON = {
  // Images
  jpg: "FileImage", jpeg: "FileImage", png: "FileImage", gif: "FileImage",
  webp: "FileImage", bmp: "FileImage", svg: "FileImage", ico: "FileImage",
  tiff: "FileImage", tif: "FileImage", heic: "FileImage", heif: "FileImage",
  raw: "FileImage", avif: "FileImage", psd: "FileImage", ai: "FileImage",
  // Video
  mp4: "FileVideo", mov: "FileVideo", avi: "FileVideo", mkv: "FileVideo",
  webm: "FileVideo", flv: "FileVideo", wmv: "FileVideo", m4v: "FileVideo",
  mpeg: "FileVideo", mpg: "FileVideo", "3gp": "FileVideo",
  // Audio
  mp3: "FileAudio", wav: "FileAudio", flac: "FileAudio", aac: "FileAudio",
  ogg: "FileAudio", m4a: "FileAudio", wma: "FileAudio", opus: "FileAudio",
  mid: "FileAudio", midi: "FileAudio",
  // Archives
  zip: "FileArchive", rar: "FileArchive", "7z": "FileArchive", tar: "FileArchive",
  gz: "FileArchive", bz2: "FileArchive", xz: "FileArchive", tgz: "FileArchive",
  tbz: "FileArchive", zst: "FileArchive", lz: "FileArchive", lzma: "FileArchive",
  // Code / markup / config
  js: "FileCode", mjs: "FileCode", cjs: "FileCode", ts: "FileCode",
  jsx: "FileCode", tsx: "FileCode", py: "FileCode", rb: "FileCode",
  go: "FileCode", rs: "FileCode", java: "FileCode", cs: "FileCode",
  cpp: "FileCode", cc: "FileCode", c: "FileCode", h: "FileCode",
  hpp: "FileCode", php: "FileCode", swift: "FileCode", kt: "FileCode",
  scala: "FileCode", json: "FileCode", xml: "FileCode", yaml: "FileCode",
  yml: "FileCode", toml: "FileCode", html: "FileCode", htm: "FileCode",
  css: "FileCode", scss: "FileCode", sass: "FileCode", less: "FileCode",
  vue: "FileCode", svelte: "FileCode", lua: "FileCode", dart: "FileCode",
  sql: "FileCode",
  // Spreadsheets
  xls: "FileSpreadsheet", xlsx: "FileSpreadsheet", ods: "FileSpreadsheet",
  csv: "FileSpreadsheet", tsv: "FileSpreadsheet", numbers: "FileSpreadsheet",
  // Presentations  (.key here is Apple Keynote, not a crypto key)
  ppt: "FilePresentation", pptx: "FilePresentation", odp: "FilePresentation",
  key: "FilePresentation",
  // PDF
  pdf: "FilePdf",
  // Shell / terminal
  sh: "FileTerminal", bash: "FileTerminal", zsh: "FileTerminal",
  fish: "FileTerminal", ps1: "FileTerminal", bat: "FileTerminal",
  cmd: "FileTerminal",
  // Fonts
  ttf: "FileFont", otf: "FileFont", woff: "FileFont", woff2: "FileFont",
  eot: "FileFont",
  // E-books
  epub: "FileBook", mobi: "FileBook", azw: "FileBook", azw3: "FileBook",
  // 3D / CAD
  stl: "FileBox", obj: "FileBox", blend: "FileBox", fbx: "FileBox",
  dwg: "FileBox", dxf: "FileBox", glb: "FileBox", gltf: "FileBox",
  // Database
  db: "Database", sqlite: "Database", sqlite3: "Database", mdb: "Database",
};

function iconForFile(name) {
  const Icons = window.Icons;
  const dot = name.lastIndexOf(".");
  // Treat "no extension" and dotfiles (".gitignore") as default.
  if (dot <= 0) return Icons.FileText;
  const ext = name.slice(dot + 1).toLowerCase();
  return Icons[FILE_EXT_ICON[ext]] || Icons.FileText;
}

function NewFolderModal({ open, onClose, onCreate, lang, submitting }) {
  const [name, setName] = React.useState("");
  const [isPrivate, setIsPrivate] = React.useState(false);
  React.useEffect(() => { if (open) { setName(""); setIsPrivate(false); } }, [open]);
  const canSubmit = name.trim().length > 0 && !submitting;
  return (
    <window.Modal open={open} onClose={onClose}
      title={lang === "es" ? "Nueva carpeta" : "New folder"}
      width={420}
      footer={
        <>
          <window.Btn variant="ghost" onClick={onClose} disabled={submitting}>{lang === "es" ? "Cancelar" : "Cancel"}</window.Btn>
          <window.Btn variant="brand" onClick={() => canSubmit && onCreate({ name: name.trim(), isPrivate })} disabled={!canSubmit}>
            {submitting ? (lang === "es" ? "Creando…" : "Creating…") : (lang === "es" ? "Crear" : "Create")}
          </window.Btn>
        </>
      }
    >
      <label className="field">
        <span>{lang === "es" ? "Nombre" : "Name"} <i className="req">*</i></span>
        <input className="input" autoFocus value={name} onChange={e => setName(e.target.value)} placeholder={lang === "es" ? "Contratos 2026" : "Contracts 2026"} />
      </label>
      <label style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 12, fontSize: 13 }}>
        <input type="checkbox" checked={isPrivate} onChange={e => setIsPrivate(e.target.checked)} />
        <span>{lang === "es" ? "Privado (solo yo)" : "Private (only me)"}</span>
      </label>
    </window.Modal>
  );
}

function RenameModal({ open, item, onClose, onSave, lang, submitting }) {
  const [name, setName] = React.useState("");
  React.useEffect(() => { if (item) setName(item.name); }, [item]);
  if (!open || !item) return null;
  const canSubmit = name.trim().length > 0 && name.trim() !== item.name && !submitting;
  return (
    <window.Modal open={open} onClose={onClose}
      title={lang === "es" ? "Renombrar" : "Rename"}
      width={400}
      footer={
        <>
          <window.Btn variant="ghost" onClick={onClose} disabled={submitting}>{lang === "es" ? "Cancelar" : "Cancel"}</window.Btn>
          <window.Btn variant="brand" onClick={() => canSubmit && onSave(name.trim())} disabled={!canSubmit}>
            {submitting ? "…" : (lang === "es" ? "Guardar" : "Save")}
          </window.Btn>
        </>
      }
    >
      <label className="field">
        <span>{lang === "es" ? "Nuevo nombre" : "New name"}</span>
        <input className="input" autoFocus value={name} onChange={e => setName(e.target.value)} />
      </label>
    </window.Modal>
  );
}

window.StorageScreen = function StorageScreen({ t, lang, subTab, pathParts, setPathParts }) {
  const Icons = window.Icons;
  const toast = window.useToast();
  const fileInputRef = React.useRef(null);

  // The URL owns the current folder so the browser back button restores it.
  // pathParts[0] is the folder id (absent → root).
  const folderId = (pathParts && pathParts[0]) || null;
  const setFolderId = React.useCallback((id) => {
    setPathParts(id ? [id] : []);
  }, [setPathParts]);
  const [data, setData] = React.useState({ folder: null, breadcrumb: [], folders: [], files: [] });
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);

  const [showNewFolder, setShowNewFolder] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [renameTarget, setRenameTarget] = React.useState(null); // { kind: "folder"|"file", item }
  const [dragOver, setDragOver] = React.useState(false);          // OS file-drop overlay
  const [dropTargetId, setDropTargetId] = React.useState(null);   // internal drop highlight (folder id or "__breadcrumb:<id>")
  // Custom MIME type used to mark internal drags. Browsers preserve types but
  // hide payload until drop, so the dragOver handler only sees the type list.
  const INTERNAL_TYPE = "application/x-cb-storage-item";

  const auth = window.getAuth ? window.getAuth() : null;
  const myId = auth?.member?.id || null;

  const reload = React.useCallback(async () => {
    setLoading(true); setError(null);
    try {
      const res = await window.api.storage.contents(folderId);
      setData(res);
    } catch (err) {
      setError(err.message || "Error");
    } finally {
      setLoading(false);
    }
  }, [folderId]);

  React.useEffect(() => { reload(); }, [reload]);

  const handleNewFolder = async ({ name, isPrivate }) => {
    setSubmitting(true);
    try {
      await window.api.storage.createFolder({ name, parentFolderId: folderId, isPrivate });
      toast(lang === "es" ? "Carpeta creada" : "Folder created", "success");
      setShowNewFolder(false);
      await reload();
    } catch (err) {
      toast(err.message || (lang === "es" ? "No se pudo crear" : "Could not create"), "danger");
    } finally {
      setSubmitting(false);
    }
  };

  const uploadFiles = async (files) => {
    if (!files || files.length === 0) return;
    for (const f of Array.from(files)) {
      if (f.size > MAX_FILE_BYTES) {
        toast(`${f.name}: ${lang === "es" ? "supera los 50 MB" : "exceeds 50 MB"}`, "danger");
        continue;
      }
      try {
        await window.api.storage.uploadFile(f, { folderId, isPrivate: false });
        toast(`${f.name} ${lang === "es" ? "subido" : "uploaded"}`, "success");
      } catch (err) {
        toast(`${f.name}: ${err.message || "error"}`, "danger");
      }
    }
    await reload();
  };

  const onPickFiles = (e) => uploadFiles(e.target.files);

  // OS files dropped on the page → upload. Anything else (internal drag) → ignore here
  // and let the per-row drop handlers take over.
  const isOsFileDrag = (e) => Array.from(e.dataTransfer?.types || []).includes("Files");

  const onPageDragOver = (e) => {
    if (isOsFileDrag(e)) { e.preventDefault(); setDragOver(true); }
  };
  const onPageDragLeave = () => setDragOver(false);
  const onPageDrop = (e) => {
    if (!isOsFileDrag(e)) return;
    e.preventDefault(); setDragOver(false);
    uploadFiles(e.dataTransfer.files);
  };

  // ---------- Internal drag-and-drop (move between folders) ----------

  const beginInternalDrag = (e, kind, item) => {
    if (!isOwner(item)) { e.preventDefault(); return; }
    e.dataTransfer.effectAllowed = "move";
    const payload = JSON.stringify({
      kind, id: item.id,
      currentParentId: kind === "folder" ? (item.parentFolderId ?? null) : (item.folderId ?? null),
    });
    e.dataTransfer.setData(INTERNAL_TYPE, payload);
    e.dataTransfer.setData("text/plain", item.name);
    // Custom ghost + lifted-source feel (see ui-overlays.jsx). `flat` skips the
    // rotation that looks fine on small kanban cards but awkward on long rows.
    window.startDrag?.(e, e.currentTarget, { flat: true });
  };

  const allowInternalDrop = (e) => {
    if (!Array.from(e.dataTransfer.types || []).includes(INTERNAL_TYPE)) return false;
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
    return true;
  };

  const moveItem = async (payload, targetFolderId) => {
    // Same-folder no-op.
    if ((payload.currentParentId ?? null) === (targetFolderId ?? null)) return;
    // Folder-into-self no-op (server also rejects, but skip the round-trip).
    if (payload.kind === "folder" && payload.id === targetFolderId) return;

    try {
      if (payload.kind === "folder") {
        // We don't have the dragged folder's full record here (it might no longer be
        // visible after the move). Ask the server with current name + visibility from
        // the row we know — find it in `data.folders` by id.
        const f = data.folders.find(x => x.id === payload.id);
        if (!f) return;
        await window.api.storage.updateFolder(f.id, { name: f.name, parentFolderId: targetFolderId, isPrivate: f.isPrivate });
      } else {
        const f = data.files.find(x => x.id === payload.id);
        if (!f) return;
        await window.api.storage.updateFile(f.id, { name: f.name, folderId: targetFolderId, isPrivate: f.isPrivate });
      }
      toast(lang === "es" ? "Movido" : "Moved", "success");
      await reload();
    } catch (err) {
      toast(err.message || (lang === "es" ? "No se pudo mover" : "Could not move"), "danger");
    }
  };

  const onFolderRowDrop = (e, targetFolder) => {
    if (!allowInternalDrop(e)) return;
    setDropTargetId(null);
    const raw = e.dataTransfer.getData(INTERNAL_TYPE);
    if (!raw) return;
    try { moveItem(JSON.parse(raw), targetFolder.id); } catch { /* malformed payload */ }
  };

  const onBreadcrumbDrop = (e, targetId) => {
    if (!allowInternalDrop(e)) return;
    setDropTargetId(null);
    const raw = e.dataTransfer.getData(INTERNAL_TYPE);
    if (!raw) return;
    try { moveItem(JSON.parse(raw), targetId); } catch { /* */ }
  };

  const onDrop = onPageDrop; // kept for the page-level drop binding below

  const handleDownload = async (file) => {
    try { await window.api.storage.downloadFile(file.id, file.name); }
    catch (err) { toast(err.message || (lang === "es" ? "No se pudo descargar" : "Could not download"), "danger"); }
  };

  const handleDelete = async (item, kind) => {
    const label = item.name;
    const ok = window.confirm(lang === "es" ? `¿Borrar "${label}"?` : `Delete "${label}"?`);
    if (!ok) return;
    try {
      if (kind === "folder") await window.api.storage.deleteFolder(item.id);
      else await window.api.storage.deleteFile(item.id);
      toast(lang === "es" ? "Eliminado" : "Deleted", "success");
      await reload();
    } catch (err) {
      toast(err.message || (lang === "es" ? "No se pudo eliminar" : "Could not delete"), "danger");
    }
  };

  const handleRenameSave = async (newName) => {
    if (!renameTarget) return;
    setSubmitting(true);
    try {
      const { kind, item } = renameTarget;
      if (kind === "folder") {
        await window.api.storage.updateFolder(item.id, { name: newName, parentFolderId: item.parentFolderId, isPrivate: item.isPrivate });
      } else {
        await window.api.storage.updateFile(item.id, { name: newName, folderId: item.folderId, isPrivate: item.isPrivate });
      }
      toast(lang === "es" ? "Renombrado" : "Renamed", "success");
      setRenameTarget(null);
      await reload();
    } catch (err) {
      toast(err.message || (lang === "es" ? "No se pudo renombrar" : "Could not rename"), "danger");
    } finally {
      setSubmitting(false);
    }
  };

  const handleToggleVisibility = async (item, kind) => {
    try {
      if (kind === "folder") {
        await window.api.storage.updateFolder(item.id, { name: item.name, parentFolderId: item.parentFolderId, isPrivate: !item.isPrivate });
      } else {
        await window.api.storage.updateFile(item.id, { name: item.name, folderId: item.folderId, isPrivate: !item.isPrivate });
      }
      await reload();
    } catch (err) {
      toast(err.message || "Error", "danger");
    }
  };

  const breadcrumbItems = [
    { id: null, name: lang === "es" ? "Almacenamiento" : "Storage" },
    ...(data.breadcrumb || []),
    ...(data.folder ? [data.folder] : []),
  ];

  const isOwner = (item) => myId && item.ownerTeamMemberId === myId;

  // Subnav-driven view of the current folder's contents. The API returns one
  // folder at a time (current folderId), so the filters operate on that scope:
  //   - files   → default browser (folders + files)
  //   - shared  → only items with isPrivate === false
  //   - recent  → files only, sorted by updatedAt desc
  //   - trash   → handled separately below (placeholder until backend support)
  let displayFolders = data.folders;
  let displayFiles = data.files;
  if (subTab === "shared") {
    displayFolders = data.folders.filter(f => !f.isPrivate);
    displayFiles = data.files.filter(f => !f.isPrivate);
  } else if (subTab === "recent") {
    displayFolders = [];
    displayFiles = [...data.files].sort((a, b) => (b.updatedAt || "").localeCompare(a.updatedAt || ""));
  }

  return (
    <div className="page"
      onDragOver={onPageDragOver}
      onDragLeave={onPageDragLeave}
      onDrop={onPageDrop}
      style={{ position: "relative" }}
    >
      <window.PageHeader
        title={lang === "es" ? "Almacenamiento" : "Storage"}
        subtitle={lang === "es" ? "Archivos compartidos con tu equipo" : "Files shared with your team"}
        actions={
          <>
            <window.Btn variant="ghost" icon={Icons.Folder} onClick={() => setShowNewFolder(true)}>
              {lang === "es" ? "Nueva carpeta" : "New folder"}
            </window.Btn>
            <window.Btn variant="brand" icon={Icons.ArrowUp} onClick={() => fileInputRef.current?.click()}>
              {lang === "es" ? "Subir archivos" : "Upload"}
            </window.Btn>
            <input ref={fileInputRef} type="file" multiple style={{ display: "none" }} onChange={onPickFiles} />
          </>
        }
      />

      {/* Breadcrumb */}
      <div className="storage-breadcrumb" style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 12, fontSize: 13, color: "var(--text-muted)", flexWrap: "wrap" }}>
        {/* Explicit "go up" button — always visible when not at root, and acts
            as a drop target so the user can drag a file/folder back to the
            parent folder without having to spot the breadcrumb chip. */}
        {breadcrumbItems.length > 1 && (() => {
          const parent = breadcrumbItems[breadcrumbItems.length - 2];
          const highlighted = dropTargetId === "__up";
          return (
            <button
              className="btn ghost storage-up"
              style={{
                padding: "2px 8px", height: "auto", fontSize: 13, gap: 4,
                background: highlighted ? "var(--brand-soft)" : undefined,
                borderColor: highlighted ? "var(--brand-border)" : undefined,
              }}
              onClick={() => setFolderId(parent.id)}
              onDragOver={(e) => { if (allowInternalDrop(e)) setDropTargetId("__up"); }}
              onDragLeave={() => setDropTargetId(curr => curr === "__up" ? null : curr)}
              onDrop={(e) => onBreadcrumbDrop(e, parent.id)}
              title={lang === "es" ? "Subir un nivel" : "Go up"}
              aria-label={lang === "es" ? "Subir un nivel" : "Go up"}
            >
              <Icons.ArrowUp width={14} height={14} />
              <span>{lang === "es" ? "Subir" : "Up"}</span>
            </button>
          );
        })()}
        {breadcrumbItems.map((b, i) => {
          const isLast = i === breadcrumbItems.length - 1;
          const dropKey = `__breadcrumb:${b.id || "root"}`;
          const highlighted = dropTargetId === dropKey;
          return (
            <React.Fragment key={`${b.id || "root"}-${i}`}>
              {i > 0 && <span>/</span>}
              {isLast
                ? <span style={{ color: "var(--text)", fontWeight: 500 }}>{b.name}</span>
                : <button
                    className="btn ghost storage-crumb"
                    style={{
                      padding: "2px 6px", height: "auto", fontSize: 13,
                      background: highlighted ? "var(--brand-soft)" : undefined,
                      borderColor: highlighted ? "var(--brand-border)" : undefined,
                    }}
                    onClick={() => setFolderId(b.id)}
                    onDragOver={(e) => { if (allowInternalDrop(e)) setDropTargetId(dropKey); }}
                    onDragLeave={() => setDropTargetId(curr => curr === dropKey ? null : curr)}
                    onDrop={(e) => onBreadcrumbDrop(e, b.id)}
                  >{b.name}</button>}
            </React.Fragment>
          );
        })}
      </div>

      {error && (
        <div className="empty" style={{ padding: 24, marginBottom: 12 }}>
          <div className="empty-icon"><Icons.AlertTriangle /></div>
          <h3>{lang === "es" ? "Error al cargar" : "Failed to load"}</h3>
          <p>{error}</p>
          <window.Btn variant="ghost" onClick={reload}>{lang === "es" ? "Reintentar" : "Retry"}</window.Btn>
        </div>
      )}

      {!error && loading && <div style={{ padding: 24, color: "var(--text-muted)" }}>{lang === "es" ? "Cargando…" : "Loading…"}</div>}

      {!error && !loading && subTab === "trash" && (
        <div className="empty" style={{ padding: "60px 24px" }}>
          <div className="empty-icon"><Icons.Trash /></div>
          <h3>{lang === "es" ? "Papelera vacía" : "Trash is empty"}</h3>
          <p>{lang === "es" ? "Aún no soportamos enviar archivos a la papelera." : "Sending files to the trash isn't supported yet."}</p>
        </div>
      )}

      {!error && !loading && subTab !== "trash" && displayFolders.length === 0 && displayFiles.length === 0 && (
        <div className="empty" style={{ padding: "60px 24px" }}>
          <div className="empty-icon"><Icons.Folder /></div>
          <h3>{
            subTab === "shared" ? (lang === "es" ? "Nada compartido aquí" : "Nothing shared here") :
            subTab === "recent" ? (lang === "es" ? "No hay archivos recientes" : "No recent files") :
            (lang === "es" ? "Esta carpeta está vacía" : "This folder is empty")
          }</h3>
          {(subTab === "files" || !subTab) && (
            <p>{lang === "es" ? "Arrastra archivos aquí o usa el botón Subir." : "Drag files here or use the Upload button."}</p>
          )}
        </div>
      )}

      {!error && !loading && subTab !== "trash" && (displayFolders.length > 0 || displayFiles.length > 0) && (
        <div className="card" style={{ overflow: "hidden" }}>
          <table className="tbl">
            <thead>
              <tr>
                <th style={{ width: "60%" }}>{lang === "es" ? "Nombre" : "Name"}</th>
                <th>{lang === "es" ? "Tamaño" : "Size"}</th>
                <th>{lang === "es" ? "Visibilidad" : "Visibility"}</th>
                <th>{lang === "es" ? "Modificado" : "Modified"}</th>
                <th style={{ textAlign: "right" }}>{lang === "es" ? "Acciones" : "Actions"}</th>
              </tr>
            </thead>
            <tbody>
              {/* Parent-folder shortcut: a fake "folder" row at the top that
                  takes you (and any dropped item) one level up. Mirrors the
                  classic ".." entry from desktop file managers. Hidden on the
                  flat "recent" view since folder navigation isn't relevant there. */}
              {breadcrumbItems.length > 1 && subTab !== "recent" && (() => {
                const parent = breadcrumbItems[breadcrumbItems.length - 2];
                const highlighted = dropTargetId === "__up-row";
                return (
                  <tr
                    key="__up-row"
                    onDragOver={(e) => { if (allowInternalDrop(e)) setDropTargetId("__up-row"); }}
                    onDragLeave={() => setDropTargetId(curr => curr === "__up-row" ? null : curr)}
                    onDrop={(e) => onBreadcrumbDrop(e, parent.id)}
                    style={highlighted ? { background: "var(--brand-soft)", outline: "1px solid var(--brand-border)" } : undefined}
                  >
                    <td>
                      <button
                        className="btn ghost"
                        style={{ padding: 0, height: "auto", display: "flex", alignItems: "center", gap: 8 }}
                        onClick={() => setFolderId(parent.id)}
                        title={lang === "es" ? `Subir a "${parent.name}"` : `Up to "${parent.name}"`}
                      >
                        <span style={{ color: "#F97316", display: "inline-flex" }}><Icons.Undo /></span>
                        <span style={{ fontWeight: 600 }}>{parent.name}</span>
                      </button>
                    </td>
                    <td style={{ color: "var(--text-muted)" }}>—</td>
                    <td style={{ color: "var(--text-muted)" }}>—</td>
                    <td style={{ color: "var(--text-muted)" }}>—</td>
                    <td></td>
                  </tr>
                );
              })()}
              {displayFolders.map(f => {
                const highlighted = dropTargetId === f.id;
                return (
                <tr
                  key={`fld-${f.id}`}
                  draggable={isOwner(f)}
                  onDragStart={(e) => beginInternalDrag(e, "folder", f)}
                  onDragOver={(e) => { if (allowInternalDrop(e)) setDropTargetId(f.id); }}
                  onDragLeave={() => setDropTargetId(curr => curr === f.id ? null : curr)}
                  onDrop={(e) => onFolderRowDrop(e, f)}
                  style={highlighted ? { background: "var(--brand-soft)", outline: "1px solid var(--brand-border)" } : undefined}
                >
                  <td>
                    <button className="btn ghost" style={{ padding: 0, height: "auto", display: "flex", alignItems: "center", gap: 8 }} onClick={() => setFolderId(f.id)}>
                      <span style={{ color: "#F97316", display: "inline-flex" }}><Icons.Folder /></span>
                      <span style={{ fontWeight: 600 }}>{f.name}</span>
                    </button>
                  </td>
                  <td style={{ color: "var(--text-muted)" }}>—</td>
                  <td>
                    {f.isPrivate
                      ? <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--text-muted)" }}><Icons.Lock width={14} height={14} /> {lang === "es" ? "Privado" : "Private"}</span>
                      : <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--text-muted)" }}><Icons.Globe width={14} height={14} /> {lang === "es" ? "Compartido" : "Shared"}</span>
                    }
                  </td>
                  <td style={{ color: "var(--text-muted)" }}>{formatDate(f.updatedAt, lang)}</td>
                  <td style={{ textAlign: "right" }}>
                    {isOwner(f) && (
                      <div style={{ display: "inline-flex", gap: 4 }}>
                        <button className="btn ghost sm" title={lang === "es" ? "Renombrar" : "Rename"} onClick={() => setRenameTarget({ kind: "folder", item: f })}><Icons.Edit /></button>
                        <button className="btn ghost sm" title={f.isPrivate ? (lang === "es" ? "Hacer compartido" : "Make shared") : (lang === "es" ? "Hacer privado" : "Make private")} onClick={() => handleToggleVisibility(f, "folder")}>
                          {f.isPrivate ? <Icons.Globe /> : <Icons.Lock />}
                        </button>
                        <button className="btn ghost sm" title={lang === "es" ? "Eliminar" : "Delete"} onClick={() => handleDelete(f, "folder")}><Icons.Trash /></button>
                      </div>
                    )}
                  </td>
                </tr>
                );
              })}
              {displayFiles.map(f => {
                const FileIcon = iconForFile(f.name);
                return (
                <tr
                  key={`fil-${f.id}`}
                  draggable={isOwner(f)}
                  onDragStart={(e) => beginInternalDrag(e, "file", f)}
                >
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <FileIcon width={16} height={16} style={{ flexShrink: 0 }} />
                      <span>{f.name}</span>
                    </div>
                  </td>
                  <td style={{ color: "var(--text-muted)" }}>{formatSize(f.sizeBytes)}</td>
                  <td>
                    {f.isPrivate
                      ? <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--text-muted)" }}><Icons.Lock width={14} height={14} /> {lang === "es" ? "Privado" : "Private"}</span>
                      : <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--text-muted)" }}><Icons.Globe width={14} height={14} /> {lang === "es" ? "Compartido" : "Shared"}</span>
                    }
                  </td>
                  <td style={{ color: "var(--text-muted)" }}>{formatDate(f.updatedAt, lang)}</td>
                  <td style={{ textAlign: "right" }}>
                    <div style={{ display: "inline-flex", gap: 4 }}>
                      <button className="btn ghost sm" title={lang === "es" ? "Descargar" : "Download"} onClick={() => handleDownload(f)}><Icons.Download /></button>
                      {isOwner(f) && <>
                        <button className="btn ghost sm" title={lang === "es" ? "Renombrar" : "Rename"} onClick={() => setRenameTarget({ kind: "file", item: f })}><Icons.Edit /></button>
                        <button className="btn ghost sm" title={f.isPrivate ? (lang === "es" ? "Hacer compartido" : "Make shared") : (lang === "es" ? "Hacer privado" : "Make private")} onClick={() => handleToggleVisibility(f, "file")}>
                          {f.isPrivate ? <Icons.Globe /> : <Icons.Lock />}
                        </button>
                        <button className="btn ghost sm" title={lang === "es" ? "Eliminar" : "Delete"} onClick={() => handleDelete(f, "file")}><Icons.Trash /></button>
                      </>}
                    </div>
                  </td>
                </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      {/* Drag-overlay hint */}
      {dragOver && (
        <div style={{
          position: "absolute", inset: 0, background: "var(--brand-soft)", border: "2px dashed var(--brand-border)",
          borderRadius: 12, display: "flex", alignItems: "center", justifyContent: "center", pointerEvents: "none",
          fontSize: 14, color: "var(--brand)"
        }}>
          {lang === "es" ? "Suelta para subir" : "Drop to upload"}
        </div>
      )}

      <NewFolderModal open={showNewFolder} onClose={() => setShowNewFolder(false)} onCreate={handleNewFolder} lang={lang} submitting={submitting} />
      <RenameModal open={!!renameTarget} item={renameTarget?.item} onClose={() => setRenameTarget(null)} onSave={handleRenameSave} lang={lang} submitting={submitting} />
    </div>
  );
};

Object.assign(window, { StorageScreen: window.StorageScreen });
