"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import Link from "next/link";
import { useFamily } from "@/app/FamilyProvider";
import { Button, ConfirmDialog, Modal } from "@/components/ui";
import { StorageMeter, StorageQuotaBanner } from "@/components/StorageMeter";
import { formatBytes } from "@/lib/format-bytes";
import { trackMemoryAdded } from "@/lib/analytics";

/** Some Android cameras return file.type = "" — detect from extension as fallback. */
function resolveContentType(file: File): string {
  if (file.type && file.type !== "application/octet-stream") return file.type;
  const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
  const map: Record<string, string> = {
    jpg: "image/jpeg", jpeg: "image/jpeg",
    png: "image/png", webp: "image/webp",
    heic: "image/heic", heif: "image/heic",
    gif: "image/gif",
  };
  return map[ext] || "image/jpeg";
}

const PRESET_FOLDERS = [
  { id: "",            label: "All",          emoji: "🌟" },
  { id: "first-steps", label: "First Steps",  emoji: "👣" },
  { id: "bath-time",   label: "Bath Time",    emoji: "🛁" },
  { id: "feeding",     label: "Feeding",      emoji: "🍼" },
  { id: "family",      label: "Family",       emoji: "👨‍👩‍👧" },
  { id: "milestones",  label: "Milestones",   emoji: "🏆" },
  { id: "birthday",    label: "Birthday",     emoji: "🎂" },
  { id: "outings",     label: "Outings",      emoji: "🌳" },
  { id: "smiles",      label: "Smiles",       emoji: "😊" },
];

const EMOJI_OPTIONS = ["📁","🌈","⭐","🎵","🏖","🎈","🐣","💛","🌸","🦋","🐾","🎀","🌙","🏠","🎠"];

interface Folder { id: string; label: string; emoji: string; }

interface Memory {
  id: string;
  key: string;
  url: string;
  thumbnailUrl: string | null;
  sizeBytes: number | null;
  mimeType: string | null;
  title: string | null;
  description: string | null;
  takenAt: string | null;
  visionCaption: string | null;
  visionTags: string[] | null;
  isPrivate: boolean;
  processingStatus: "uploading" | "processing" | "ready" | "failed";
  createdAt: string;
}

export default function MemoriesPage() {
  const { childId } = useFamily();
  const [memories, setMemories]           = useState<Memory[]>([]);
  const [nextCursor, setNextCursor]       = useState<string | null>(null);
  const [selected, setSelected]           = useState<Memory | null>(null);
  const [uploading, setUploading]         = useState(false);
  const [uploadError, setUploadError]     = useState<string | null>(null);
  const [deleteTarget, setDeleteTarget]   = useState<string | null>(null);
  const [loadingMore, setLoadingMore]     = useState(false);
  const [activeFolder, setActiveFolder]   = useState("");
  const [showFolderPicker, setShowFolderPicker] = useState(false);
  const [pendingFolder, setPendingFolder] = useState("");
  const [query, setQuery]                 = useState("");
  const [searchResults, setSearchResults] = useState<Memory[] | null>(null);
  const [searching, setSearching]         = useState(false);

  // Custom folders
  const [customFolders, setCustomFolders] = useState<Folder[]>([]);
  const [showNewFolder, setShowNewFolder] = useState(false);
  const [newFolderName, setNewFolderName] = useState("");
  const [newFolderEmoji, setNewFolderEmoji] = useState("📁");

  // Quota state — fetched on mount, refreshed after any 402 response
  const [quotaExceeded, setQuotaExceeded]   = useState(false);
  const [quotaBanner, setQuotaBanner]       = useState<{ usedBytes: number; limitBytes: number } | null>(null);

  const fileRef   = useRef<HTMLInputElement>(null);
  const loaderRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    try {
      const saved = localStorage.getItem("tia_custom_folders");
      if (saved) setCustomFolders(JSON.parse(saved));
    } catch {}
  }, []);

  // Fetch storage quota on mount so we can disable the FAB before the user
  // tries to upload and hits a 402 (better UX than a failed upload).
  useEffect(() => {
    fetch("/api/storage-usage")
      .then(r => r.json())
      .then(d => {
        if (d.exceeded) {
          setQuotaExceeded(true);
          setQuotaBanner({ usedBytes: d.usedBytes, limitBytes: d.limitBytes });
        }
      })
      .catch(() => {});
  }, []);

  const allFolders: Folder[] = [...PRESET_FOLDERS, ...customFolders];

  const handleCreateFolder = () => {
    const trimmed = newFolderName.trim();
    if (!trimmed) return;
    const id = trimmed.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") || `folder-${Date.now()}`;
    if (allFolders.some(f => f.id === id)) { alert("A folder with this name already exists."); return; }
    const folder: Folder = { id, label: trimmed, emoji: newFolderEmoji };
    const updated = [...customFolders, folder];
    setCustomFolders(updated);
    localStorage.setItem("tia_custom_folders", JSON.stringify(updated));
    setNewFolderName("");
    setNewFolderEmoji("📁");
    setShowNewFolder(false);
  };

  const fetchMemories = useCallback(async (cursor?: string) => {
    if (!childId) return;
    try {
      const params = new URLSearchParams({ childId, limit: "60" });
      if (cursor) params.set("cursor", cursor);
      const res  = await fetch(`/api/memories?${params}`);
      const data = await res.json();
      if (cursor) setMemories(prev => [...prev, ...(data.items || [])]);
      else        setMemories(data.items || []);
      setNextCursor(data.nextCursor || null);
    } catch (err) { console.error("Failed to fetch memories:", err); }
  }, [childId]);

  useEffect(() => { if (childId) fetchMemories(); }, [childId, fetchMemories]);

  useEffect(() => {
    if (!loaderRef.current) return;
    const observer = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting && nextCursor && !loadingMore) {
        setLoadingMore(true);
        fetchMemories(nextCursor).finally(() => setLoadingMore(false));
      }
    });
    observer.observe(loaderRef.current);
    return () => observer.disconnect();
  }, [nextCursor, loadingMore, fetchMemories]);

  const handleUploadClick = () => setShowFolderPicker(true);

  const handleFolderChosen = (folderId: string) => {
    setPendingFolder(folderId);
    setShowFolderPicker(false);
    fileRef.current?.click();
  };

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file || !childId) return;

    const contentType = resolveContentType(file);
    setUploading(true);
    setUploadError(null);

    try {
      // Step 1: reserve slot + quota gate
      const initRes = await fetch("/api/upload", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ filename: file.name, contentType, childId, sizeBytes: file.size }),
      });
      const initData = await initRes.json();
      if (!initRes.ok) {
        if (initRes.status === 402 && initData.reason === "storage_quota_exceeded") {
          setQuotaExceeded(true);
          setQuotaBanner({ usedBytes: initData.usedBytes ?? 0, limitBytes: initData.limitBytes ?? 0 });
          setUploadError("Storage quota exceeded");
        } else {
          setUploadError(initData.error || `Upload failed (${initRes.status})`);
        }
        return;
      }
      const { key, memoryId } = initData;

      // Step 2: binary upload via proxy
      const putRes = await fetch(`/api/upload?${new URLSearchParams({ key, contentType })}`, {
        method: "PUT", body: file, headers: { "Content-Type": contentType },
      });
      if (!putRes.ok) { setUploadError(`Upload failed (${putRes.status})`); return; }

      // Assign folder if chosen
      if (pendingFolder) {
        await fetch(`/api/memories/${memoryId}`, {
          method: "PATCH",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ description: pendingFolder }),
        }).catch(() => {});
      }

      // Step 3: confirm
      const confirmRes = await fetch(`/api/memories/${memoryId}/confirm`, { method: "POST" });
      if (!confirmRes.ok) {
        const cd = await confirmRes.json().catch(() => ({})) as { reason?: string; usedBytes?: number; limitBytes?: number; error?: string };
        if (confirmRes.status === 402) {
          setQuotaExceeded(true);
          fetch("/api/storage-usage").then(r => r.json())
            .then(d => setQuotaBanner({ usedBytes: d.usedBytes, limitBytes: d.limitBytes }))
            .catch(() => {});
          setUploadError("Storage quota exceeded");
        } else {
          setUploadError(cd.error || `Failed to confirm upload (${confirmRes.status})`);
        }
        return;
      }

      trackMemoryAdded();
      // Optimistic grid update
      const proxyUrl = `/api/img?key=${encodeURIComponent(key)}`;
      setMemories(prev => [{
        id: memoryId, key, url: proxyUrl, thumbnailUrl: null,
        sizeBytes: file.size, mimeType: contentType, title: null,
        description: pendingFolder || null, takenAt: null,
        visionCaption: null, visionTags: null, isPrivate: false,
        processingStatus: "processing", createdAt: new Date().toISOString(),
      }, ...prev]);
    } catch (err) {
      setUploadError(err instanceof Error ? err.message : "Upload failed");
    } finally {
      setUploading(false);
      setPendingFolder("");
      if (fileRef.current) fileRef.current.value = "";
    }
  };

  const handleDelete = async (id: string) => {
    await fetch(`/api/memories/${id}`, { method: "DELETE" });
    setMemories(prev => prev.filter(m => m.id !== id));
    if (selected?.id === id) setSelected(null);
    setDeleteTarget(null);
  };

  const handleSearch = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!query.trim()) { setSearchResults(null); return; }
    setSearching(true);
    try {
      const res = await fetch("/api/memories/search", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ query: query.trim(), childId }),
      });
      const data = await res.json();
      setSearchResults(data.items || []);
    } catch { setSearchResults([]); }
    setSearching(false);
  };

  const baseList = searchResults !== null ? searchResults : memories;
  const displayMemories = (activeFolder
    ? baseList.filter(m => m.description === activeFolder)
    : baseList
  ).filter(m => m.url || m.thumbnailUrl);

  const folderCounts = memories.reduce<Record<string, number>>((acc, m) => {
    if (m.description) acc[m.description] = (acc[m.description] || 0) + 1;
    return acc;
  }, {});

  return (
    <div className="min-h-screen bg-gray-50 dark:bg-gray-900">
      {/* Header */}
      <div className="sticky top-0 z-10 bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800 px-4 py-3 space-y-2">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Link href="/menu" className="text-gray-500 dark:text-gray-400 p-1">←</Link>
            <h1 className="text-xs font-semibold dark:text-white">Memories 📸</h1>
          </div>
        </div>
        {/* Storage meter — only visible when approaching or exceeded */}
        <StorageMeter compact className="px-1" />
      </div>

      {/* Storage quota banner — shown when upload is blocked */}
      {quotaBanner && (
        <div className="px-4 pt-3">
          <StorageQuotaBanner
            usedBytes={quotaBanner.usedBytes}
            limitBytes={quotaBanner.limitBytes}
            usedFormatted={formatBytes(quotaBanner.usedBytes)}
            limitFormatted={formatBytes(quotaBanner.limitBytes)}
          />
        </div>
      )}

      {/* Search */}
      <form onSubmit={handleSearch} className="px-4 pt-3 pb-1 flex gap-2">
        <input
          value={query}
          onChange={e => setQuery(e.target.value)}
          placeholder="Search photos..."
          className="flex-1 px-3 py-2 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm dark:text-white dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-rose-300"
        />
        <Button type="submit" size="sm" loading={searching}>Search</Button>
        {searchResults !== null && (
          <Button type="button" variant="ghost" size="sm" onClick={() => { setSearchResults(null); setQuery(""); }}>Clear</Button>
        )}
      </form>

      {/* Folder pills */}
      <div className="px-4 py-2 flex gap-2 overflow-x-auto scrollbar-hide">
        {allFolders.map(folder => {
          const count = folder.id === "" ? memories.length : (folderCounts[folder.id] || 0);
          const isActive = activeFolder === folder.id;
          return (
            <button
              key={folder.id}
              onClick={() => setActiveFolder(folder.id)}
              className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm whitespace-nowrap flex-shrink-0 transition-colors ${
                isActive
                  ? "bg-rose-400 text-white"
                  : "bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-300 border border-gray-200 dark:border-gray-700"
              }`}
            >
              <span>{folder.emoji}</span>
              <span>{folder.label}</span>
              {count > 0 && (
                <span className={`text-xs ${isActive ? "text-white/80" : "text-gray-400"}`}>{count}</span>
              )}
            </button>
          );
        })}
        {/* New folder button */}
        <button
          onClick={() => setShowNewFolder(true)}
          className="flex items-center gap-1 px-3 py-1.5 rounded-full text-sm whitespace-nowrap flex-shrink-0 bg-white dark:bg-gray-800 text-gray-400 dark:text-gray-500 border border-dashed border-gray-300 dark:border-gray-600 hover:border-rose-300 hover:text-rose-400 transition-colors"
        >
          <span>+</span>
          <span>New</span>
        </button>
      </div>

      {/* Gallery grid */}
      <div className="px-3 pb-24">
        {displayMemories.length === 0 ? (
          <div className="flex flex-col items-center justify-center py-20 text-center gap-3">
            <span className="text-6xl">📷</span>
            <p className="font-medium text-gray-500 dark:text-gray-400">
              {searchResults !== null ? "No photos found" : activeFolder ? "No photos in this folder yet" : "No memories yet"}
            </p>
            <p className="text-sm text-gray-400 dark:text-gray-500">
              {searchResults !== null ? "Try a different search term." : "Tap + to add your first photo."}
            </p>
          </div>
        ) : (
          <div className="grid grid-cols-4 gap-1 pt-1">
            {displayMemories.map(mem => (
              <MemoryTile
                key={mem.id}
                memory={mem}
                folder={allFolders.find(f => f.id === mem.description) ?? null}
                onClick={() => setSelected(mem)}
              />
            ))}
          </div>
        )}
        <div ref={loaderRef} className="py-4 text-center text-sm text-gray-400">
          {loadingMore && "Loading more..."}
        </div>
      </div>

      {/* Upload error banner */}
      {uploadError && !uploading && (
        <div className="fixed top-4 inset-x-4 z-50 bg-red-500 text-white rounded-xl px-4 py-3 flex items-center justify-between shadow-lg">
          <span className="text-sm">{uploadError}</span>
          <button onClick={() => setUploadError(null)} className="ml-3 text-white/80 hover:text-white text-lg leading-none">✕</button>
        </div>
      )}

      {/* Upload FAB — disabled when storage quota is exceeded */}
      <div className="fixed bottom-24 right-6 z-50">
        <button
          onClick={quotaExceeded ? undefined : handleUploadClick}
          disabled={uploading || quotaExceeded}
          title={quotaExceeded ? "Storage full — delete some memories or upgrade to upload more" : undefined}
          className={`w-14 h-14 rounded-full shadow-xl flex items-center justify-center text-white text-2xl font-light transition-colors ${
            uploading || quotaExceeded ? "bg-gray-400 cursor-not-allowed" : "bg-rose-400 hover:bg-rose-500 active:bg-rose-600"
          }`}
        >
          {uploading ? "…" : quotaExceeded ? "⊘" : "+"}
        </button>
        <input ref={fileRef} type="file" accept="image/*" onChange={handleUpload} className="hidden" disabled={uploading || quotaExceeded} />
      </div>

      {/* Folder picker modal */}
      <Modal open={showFolderPicker} onClose={() => setShowFolderPicker(false)} title="Add to folder" maxWidth="sm">
        <div className="grid grid-cols-3 gap-2">
          {allFolders.map(folder => (
            <button
              key={folder.id}
              onClick={() => handleFolderChosen(folder.id)}
              className="flex flex-col items-center gap-1 p-3 rounded-xl bg-gray-50 dark:bg-gray-700 hover:bg-rose-50 dark:hover:bg-rose-900/30 transition-colors"
            >
              <span className="text-2xl">{folder.emoji}</span>
              <span className="text-xs text-center text-gray-600 dark:text-gray-300 font-medium leading-tight">{folder.label}</span>
            </button>
          ))}
          <button
            onClick={() => { setShowFolderPicker(false); setShowNewFolder(true); }}
            className="flex flex-col items-center gap-1 p-3 rounded-xl border-2 border-dashed border-gray-200 dark:border-gray-600 hover:border-rose-300 dark:hover:border-rose-700 text-gray-400 hover:text-rose-400 transition-colors"
          >
            <span className="text-2xl">➕</span>
            <span className="text-xs font-medium">New folder</span>
          </button>
        </div>
        <p className="text-xs text-gray-400 text-center mt-3">Tap "All" to upload without a folder</p>
      </Modal>

      {/* New folder modal */}
      <Modal open={showNewFolder} onClose={() => { setShowNewFolder(false); setNewFolderName(""); setNewFolderEmoji("📁"); }} title="Create folder" maxWidth="sm">
        <div className="space-y-3">
          <div>
            <p className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">Choose an emoji</p>
            <div className="flex flex-wrap gap-2">
              {EMOJI_OPTIONS.map(em => (
                <button
                  key={em}
                  onClick={() => setNewFolderEmoji(em)}
                  className={`text-xl p-1.5 rounded-lg transition-colors ${
                    newFolderEmoji === em
                      ? "bg-rose-100 dark:bg-rose-900 ring-2 ring-rose-400"
                      : "bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600"
                  }`}
                >
                  {em}
                </button>
              ))}
            </div>
          </div>
          <div>
            <p className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Folder name</p>
            <input
              autoFocus
              value={newFolderName}
              onChange={e => setNewFolderName(e.target.value)}
              onKeyDown={e => e.key === "Enter" && handleCreateFolder()}
              placeholder="e.g. Park days"
              maxLength={30}
              className="w-full px-3 py-2 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm dark:text-white focus:outline-none focus:ring-2 focus:ring-rose-300"
            />
          </div>
          <div className="flex gap-2 pt-1">
            <Button variant="secondary" fullWidth onClick={() => { setShowNewFolder(false); setNewFolderName(""); setNewFolderEmoji("📁"); }}>Cancel</Button>
            <Button variant="primary" fullWidth onClick={handleCreateFolder} disabled={!newFolderName.trim()}>
              {newFolderEmoji} Create
            </Button>
          </div>
        </div>
      </Modal>

      {/* Fullscreen viewer */}
      {selected && (
        <MemoryViewer
          memory={selected}
          allFolders={allFolders}
          onClose={() => setSelected(null)}
          onDelete={() => setDeleteTarget(selected.id)}
          onUpdate={updated => {
            setMemories(prev => prev.map(m => m.id === updated.id ? { ...m, ...updated } : m));
            setSelected(prev => prev?.id === updated.id ? { ...prev, ...updated } : prev);
          }}
        />
      )}

      <ConfirmDialog
        open={!!deleteTarget}
        onClose={() => setDeleteTarget(null)}
        onConfirm={() => deleteTarget && handleDelete(deleteTarget)}
        title="Delete this photo?"
        description="This removes the photo permanently from your memories."
        confirmLabel="Delete"
        variant="danger"
      />
    </div>
  );
}

// ─── Grid tile ────────────────────────────────────────────────────────────────

function MemoryTile({ memory, folder, onClick }: { memory: Memory; folder: Folder | null; onClick: () => void }) {
  const [loaded, setLoaded]   = useState(false);
  const [imgError, setImgError] = useState(false);
  const src = memory.thumbnailUrl || memory.url;

  if (imgError) return null;

  return (
    <div className="flex flex-col gap-0.5">
      <button
        className="relative w-full aspect-square rounded-xl overflow-hidden group bg-gray-100 dark:bg-gray-800 shadow-sm ring-1 ring-black/5 dark:ring-white/5"
        onClick={onClick}
      >
        <img
          src={src}
          alt={memory.title || "Memory"}
          className={`w-full h-full object-cover transition-all duration-300 group-hover:scale-110 ${loaded ? "opacity-100" : "opacity-0"}`}
          onLoad={() => setLoaded(true)}
          onError={() => setImgError(true)}
          loading="lazy"
        />
        {!loaded && <div className="absolute inset-0 bg-gray-200 dark:bg-gray-700 animate-pulse rounded-xl" />}
        {/* hover overlay */}
        <div className="absolute inset-0 bg-black/0 group-hover:bg-black/25 transition-colors duration-200" />
        {/* hover expand icon */}
        <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
          <span className="text-white text-lg drop-shadow">⤢</span>
        </div>
        {memory.processingStatus === "processing" && (
          <span className="absolute top-1 left-1 text-[8px] bg-black/60 text-white px-1.5 py-0.5 rounded-full leading-tight">
            ⏳
          </span>
        )}
        {memory.isPrivate && <span className="absolute top-1 right-1 text-[9px]">🔒</span>}
      </button>
      {/* Folder label */}
      {folder && folder.id && (
        <p className="text-[9px] text-center text-gray-400 dark:text-gray-500 truncate leading-tight px-0.5">
          {folder.emoji} {folder.label}
        </p>
      )}
    </div>
  );
}

// ─── Fullscreen viewer ────────────────────────────────────────────────────────

function MemoryViewer({ memory, allFolders, onClose, onDelete, onUpdate }: {
  memory: Memory;
  allFolders: Folder[];
  onClose: () => void;
  onDelete: () => void;
  onUpdate: (updated: Partial<Memory> & { id: string }) => void;
}) {
  const [isPrivate, setIsPrivate]         = useState(memory.isPrivate);
  const [toggling, setToggling]           = useState(false);
  const [showChrome, setShowChrome]       = useState(true);
  const [zoomed, setZoomed]               = useState(false);
  const [folderEditing, setFolderEditing] = useState(false);

  const togglePrivate = async () => {
    setToggling(true);
    const newVal = !isPrivate;
    try {
      await fetch(`/api/memories/${memory.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ isPrivate: newVal }),
      });
      setIsPrivate(newVal);
      onUpdate({ id: memory.id, isPrivate: newVal, ...(newVal ? { visionCaption: null, visionTags: null } : {}) });
    } finally { setToggling(false); }
  };

  const assignFolder = async (folderId: string) => {
    try {
      await fetch(`/api/memories/${memory.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ description: folderId || null }),
      });
      onUpdate({ id: memory.id, description: folderId || null });
    } catch (err) { console.error("Failed to assign folder:", err); }
    setFolderEditing(false);
  };

  const currentFolder = allFolders.find(f => f.id === memory.description);

  const handleImageTap = () => {
    if (zoomed) {
      setZoomed(false);
    } else {
      setShowChrome(s => !s);
    }
  };

  return (
    <div className="fixed inset-0 bg-black z-50 flex flex-col" onClick={e => e.target === e.currentTarget && onClose()}>
      {/* Top bar */}
      <div className={`flex items-center justify-between px-4 py-3 flex-shrink-0 bg-gradient-to-b from-black/70 to-transparent transition-opacity duration-200 ${showChrome ? "opacity-100" : "opacity-0 pointer-events-none"}`}>
        <button onClick={onClose} className="text-white/90 hover:text-white p-1 text-lg">✕</button>
        <div className="flex items-center gap-3">
          <button
            onClick={() => setFolderEditing(true)}
            className="text-sm text-white/70 hover:text-white flex items-center gap-1"
          >
            {currentFolder && currentFolder.id ? `${currentFolder.emoji} ${currentFolder.label}` : "📁 Folder"}
          </button>
          <button
            onClick={togglePrivate}
            disabled={toggling}
            className={`text-sm px-3 py-1 rounded-full transition-colors ${
              isPrivate ? "bg-gray-700 text-gray-200" : "bg-gray-800 text-gray-400 hover:text-white"
            }`}
          >
            {toggling ? "…" : isPrivate ? "🔒" : "🌐"}
          </button>
          <button onClick={onDelete} className="text-red-400 hover:text-red-300 p-1">🗑</button>
        </div>
      </div>

      {/* Image area */}
      <div
        className="flex-1 flex items-center justify-center min-h-0 overflow-hidden"
        onClick={handleImageTap}
      >
        <img
          src={memory.url}
          alt={memory.title || "Memory"}
          style={{ transform: zoomed ? "scale(2)" : "scale(1)", transition: "transform 0.25s ease-out" }}
          className={`max-w-full max-h-full object-contain ${zoomed ? "cursor-zoom-out" : "cursor-zoom-in"}`}
        />
      </div>

      {/* Zoom hint */}
      {showChrome && !zoomed && (
        <div className="absolute bottom-20 left-1/2 -translate-x-1/2 text-white/40 text-xs pointer-events-none">
          tap to hide UI · tap again to zoom
        </div>
      )}

      {/* Caption/tags */}
      {showChrome && !zoomed && (memory.visionCaption || (memory.visionTags && memory.visionTags.length > 0)) && (
        <div className="px-4 py-3 bg-gradient-to-t from-black/80 to-transparent flex-shrink-0">
          {memory.visionCaption && (
            <p className="text-white/90 text-sm leading-relaxed mb-2">{memory.visionCaption}</p>
          )}
          {memory.visionTags && memory.visionTags.length > 0 && (
            <div className="flex flex-wrap gap-1">
              {memory.visionTags.map((tag, i) => (
                <span key={i} className="text-xs bg-white/10 text-white/70 px-2 py-0.5 rounded-full">#{tag}</span>
              ))}
            </div>
          )}
        </div>
      )}

      {/* Folder assignment bottom sheet */}
      {folderEditing && (
        <div className="absolute inset-0 bg-black/70 flex items-end z-10" onClick={() => setFolderEditing(false)}>
          <div className="w-full bg-white dark:bg-gray-900 rounded-t-2xl p-4 max-h-[70vh] overflow-y-auto" onClick={e => e.stopPropagation()}>
            <h3 className="font-semibold text-center mb-3 dark:text-white">Move to folder</h3>
            <div className="grid grid-cols-4 gap-2">
              {allFolders.map(folder => (
                <button
                  key={folder.id}
                  onClick={() => assignFolder(folder.id)}
                  className={`flex flex-col items-center gap-1 p-2 rounded-xl transition-colors ${
                    memory.description === folder.id
                      ? "bg-rose-100 dark:bg-rose-900 ring-2 ring-rose-400"
                      : "bg-gray-100 dark:bg-gray-800"
                  }`}
                >
                  <span className="text-xl">{folder.emoji}</span>
                  <span className="text-xs text-center text-gray-600 dark:text-gray-300 leading-tight">{folder.label}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
