/* gallery.jsx — photo grid, filter bar, lightbox, compare. */

// React.memo: chỉ ảnh nào đổi mới render lại (album lớn không re-render cả lưới).
const PhotoCard = React.memo(function PhotoCard({ photo, src, full, idx, role, onOpen, onHover, onLike, onStars, onStatus, compareMode, compareSel, onCompareToggle }) {
  const marked = photo.status !== "none" || photo.liked || photo.stars > 0;
  const flag = photo.status === "pick" ? "pick" : null;
  return (
    <div className={"photo" + (photo.status !== "none" ? " sel-" + photo.status : "") + (marked ? " has-marks" : "")}
      onClick={() => compareMode ? onCompareToggle(photo.id) : onOpen(idx)}
      onMouseEnter={() => onHover && onHover(photo.id)} onMouseLeave={() => onHover && onHover(null)}
      style={compareMode && compareSel ? { outline: "3px solid var(--accent)", outlineOffset: "-3px" } : null}>
      <img src={src} alt={photo.filename} loading="lazy" decoding="async" draggable="false"
        onError={(e) => { if (full && e.currentTarget.dataset.fb !== "1") { e.currentTarget.dataset.fb = "1"; e.currentTarget.src = full; } }} />
      {flag && <div className={"corner-flag " + flag}><Icon name="check" size={13} className="ck" sw={2.4} /></div>}
      {role === "studio" && !compareMode && ((photo.picks || 0) > 0 || (photo.likes || 0) > 0 || (photo.otherStars || 0) > 0) && (
        <div title="Lựa chọn của khách" style={{ position: "absolute", top: 8, right: 8, zIndex: 4, display: "flex", alignItems: "center", gap: 5, padding: "3px 8px", borderRadius: 999, background: "var(--accent)", color: "#fff", fontSize: 11, fontWeight: 700, boxShadow: "0 2px 8px rgba(0,0,0,0.28)", pointerEvents: "none" }}>
          {(photo.picks || 0) > 0 && <Icon name="check" size={12} sw={2.6} />}
          {(photo.otherStars || 0) > 0 && <span>{photo.otherStars}★</span>}
          {(photo.likes || 0) > 0 && <Icon name="heart" size={12} fill />}
          <span>Khách</span>
        </div>
      )}
      <div className="photo-overlay" />
      {compareMode ? (
        <div className="photo-top">
          <span className="idx-badge">{compareSel ? "✓ đã chọn" : "Chạm để so sánh"}</span>
        </div>
      ) : (
        <>
          <div className="photo-top">
            <span className="idx-badge">{photo.filename}</span>
            <HeartBtn liked={photo.liked} onClick={() => onLike(photo.id)} />
          </div>
          <div className="photo-bottom">
            <Stars value={photo.stars} onChange={(n) => onStars(photo.id, n)} size={15} />
            <StatusRow status={photo.status} onSet={(s) => onStatus(photo.id, s)} />
          </div>
          {photo.note && <span className="note-dot" title="Có ghi chú" />}
        </>
      )}
    </div>
  );
});

const FILTERS = [
  { id: "all", label: "Tất cả", ic: "grid" },
  { id: "liked", label: "Yêu thích", ic: "heart" },
  { id: "pick", label: "Đã chọn", ic: "check" },
  { id: "reject", label: "Đã bỏ", ic: "x" },
  { id: "unrated", label: "Chưa xem", ic: "eye" },
];

const PAGE = 60;   // số ảnh render mỗi đợt (infinite scroll cho album lớn)

function matchFilter(p, f) {
  if (f === "all") return true;
  if (f === "liked") return p.liked;
  if (f === "unrated") return p.stars === 0 && p.status === "none" && !p.liked;
  if (f === "clientpick") return (p.picks || 0) > 0;   // khách đã chọn
  return p.status === f;
}

function Gallery({ projectId, store, role }) {
  const all = store.projectPhotos(projectId);
  const project = store.project(projectId);
  // studio có thêm bộ lọc "Khách chọn" (ảnh khách đã pick)
  const FILT = role === "studio" ? FILTERS.concat([{ id: "clientpick", label: "Khách chọn", ic: "check" }]) : FILTERS;
  const [filter, setFilter] = React.useState("all");
  const [album, setAlbum] = React.useState("__all");
  const [sort, setSort] = React.useState("recent");
  const [query, setQuery] = React.useState("");
  const [dquery, setDquery] = React.useState("");
  const [minStars, setMinStars] = React.useState(0);
  const [lbIdx, setLbIdx] = React.useState(-1);
  const [hoveredId, setHoveredId] = React.useState(null);
  const [compareMode, setCompareMode] = React.useState(false);
  const [compareSel, setCompareSel] = React.useState([]);
  const [visible, setVisible] = React.useState(PAGE);
  const toast = useToast();

  // store đổi tham chiếu mỗi lần dữ liệu đổi → giữ qua ref để callback ổn định (React.memo hiệu quả)
  const storeRef = React.useRef(store); storeRef.current = store;

  // debounce ô tìm kiếm — gõ không giật
  React.useEffect(() => { const t = setTimeout(() => setDquery(query), 180); return () => clearTimeout(t); }, [query]);

  const counts = React.useMemo(() => {
    const c = {}; FILT.forEach((f) => c[f.id] = all.filter((p) => matchFilter(p, f.id)).length); return c;
  }, [all, role]);

  const list = React.useMemo(() => {
    const q = dquery.trim().toLowerCase();
    const out = all.filter((p) => (album === "__all" || p.albumId === album) && matchFilter(p, filter)
      && p.stars >= minStars && (!q || p.filename.toLowerCase().includes(q)));
    out.sort((a, b) => {
      if (sort === "stars") return b.stars - a.stars || (b.liked - a.liked);
      if (sort === "name") return a.filename.localeCompare(b.filename);
      return 0; // recent = thứ tự nạp
    });
    return out;
  }, [all, album, filter, minStars, dquery, sort]);

  // reset số ảnh hiển thị khi đổi bộ lọc/album/sort/tìm kiếm.
  // QUAN TRỌNG: phụ thuộc all.length (số ổn định), KHÔNG phải all (mảng mới mỗi render).
  React.useEffect(() => { setVisible(PAGE); }, [album, filter, minStars, dquery, sort, all.length]);

  // infinite scroll: nạp thêm khi sentinel vào gần viewport.
  // deps có cả `visible` → sau mỗi đợt, observer gắn lại và tiếp tục nạp nếu sentinel còn trong tầm.
  const sentinelRef = React.useRef(null);
  React.useEffect(() => {
    const el = sentinelRef.current; if (!el) return;
    const io = new IntersectionObserver((ents) => {
      if (ents[0].isIntersecting) setVisible((v) => (v < list.length ? Math.min(list.length, v + PAGE) : v));
    }, { rootMargin: "800px" });
    io.observe(el);
    return () => io.disconnect();
  }, [list.length, visible]);

  const openLb = React.useCallback((i) => setLbIdx(i), []);
  const toggleCompare = React.useCallback((id) => {
    setCompareSel((s) => s.includes(id) ? s.filter((x) => x !== id) : s.length >= 2 ? [s[1], id] : [...s, id]);
  }, []);
  const onLike = React.useCallback((id) => storeRef.current.toggleLike(id), []);
  const onStars = React.useCallback((id, n) => storeRef.current.setStars(id, n), []);
  const onStatus = React.useCallback((id, s) => storeRef.current.setStatus(id, s), []);

  // phím tắt trên ảnh đang hover
  React.useEffect(() => {
    if (lbIdx >= 0) return;
    const h = (e) => {
      const tag = e.target.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
      if (!hoveredId) return;
      const s = storeRef.current;
      if (e.key >= "1" && e.key <= "5") s.setStars(hoveredId, +e.key);
      else if (e.key === "0") s.setStars(hoveredId, 0);
      else if (e.key === " ") { e.preventDefault(); s.setStatus(hoveredId, "pick"); }
      else if (e.key === "Delete" || e.key === "Backspace") { e.preventDefault(); s.setStatus(hoveredId, "reject"); }
      else if (e.key.toLowerCase() === "f") s.toggleLike(hoveredId);
    };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [hoveredId, lbIdx]);

  const shown = list.slice(0, visible);

  return (
    <div>
      {/* album strip */}
      {project.albums.length > 1 && (
        <div className="albums-strip" style={{ marginBottom: 14 }}>
          <button className={"album-chip" + (album === "__all" ? " on" : "")} onClick={() => setAlbum("__all")}>
            <Icon name="layers" size={14} /> Tất cả <span className="album-count">{all.length}</span>
          </button>
          {project.albums.map((al) => (
            <button key={al.id} className={"album-chip" + (album === al.id ? " on" : "")} onClick={() => setAlbum(al.id)}>
              <Icon name="folder" size={14} /> {al.name}
              <span className="album-count">{all.filter((p) => p.albumId === al.id).length}</span>
            </button>
          ))}
        </div>
      )}

      <div className="toolbar">
        <div className="seg">
          {FILT.map((f) => (
            <button key={f.id} className={filter === f.id ? "on" : ""} onClick={() => setFilter(f.id)}>
              <Icon name={f.ic} size={14} /> {f.label} <span className="count">{counts[f.id]}</span>
            </button>
          ))}
        </div>
        <div className="spacer" />
        <div className="field" title="Lọc theo sao tối thiểu">
          <Stars value={minStars} onChange={(n) => setMinStars(minStars === n ? 0 : n)} size={15} onLight />
        </div>
        <div className="field">
          <Icon name="search" size={16} />
          <input placeholder="Tìm tên ảnh…" value={query} onChange={(e) => setQuery(e.target.value)} />
        </div>
        <div className="field">
          <select value={sort} onChange={(e) => setSort(e.target.value)}>
            <option value="recent">Mới nhất</option>
            <option value="stars">Sao cao → thấp</option>
            <option value="name">Tên ảnh A→Z</option>
          </select>
        </div>
        <button className={"btn btn-sm " + (compareMode ? "btn-primary" : "btn-ghost")}
          onClick={() => { setCompareMode((v) => !v); setCompareSel([]); }}>
          <Icon name="compare" size={15} /> So sánh
        </button>
      </div>

      {!compareMode && (
        <div className="kbd-hint">
          <Icon name="info" size={14} style={{ color: "var(--accent)" }} />
          Mẹo: di chuột vào ảnh rồi bấm <kbd>1–5</kbd> chấm sao · <kbd>Space</kbd> chọn · <kbd>Del</kbd> bỏ · <kbd>F</kbd> yêu thích
        </div>
      )}

      {compareMode && (
        <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "0 0 16px", color: "var(--ink-2)", fontSize: 13.5 }}>
          <Icon name="info" size={16} style={{ color: "var(--accent)" }} />
          Chạm 2 ảnh để mở khung so sánh cạnh nhau ({compareSel.length}/2)
          <button className="btn btn-sm btn-quiet" onClick={() => { setCompareMode(false); setCompareSel([]); }} style={{ marginLeft: 4 }}>Thoát</button>
        </div>
      )}

      {list.length === 0 ? (
        <div className="empty">
          <Icon name="image" size={52} className="em-ic" sw={1.2} />
          <h3>Không có ảnh nào khớp bộ lọc</h3>
          <p>Thử đổi album, bộ lọc hoặc số sao tối thiểu.</p>
        </div>
      ) : (
        <>
          <div className="grid">
            {shown.map((p, i) => (
              <PhotoCard key={p.id} photo={p} src={store.thumb(p, 480)} full={p.url} idx={i} role={role}
                onOpen={openLb} onHover={setHoveredId} onLike={onLike} onStars={onStars} onStatus={onStatus}
                compareMode={compareMode} compareSel={compareSel.includes(p.id)} onCompareToggle={toggleCompare} />
            ))}
          </div>
          {visible < list.length && <div ref={sentinelRef} style={{ height: 1 }} aria-hidden="true" />}
          {visible < list.length && (
            <div style={{ textAlign: "center", padding: "18px 0", color: "var(--ink-3)", fontSize: 13 }}>
              Đang tải thêm… ({shown.length}/{list.length})
            </div>
          )}
        </>
      )}

      {lbIdx >= 0 && (
        <Lightbox list={list} index={lbIdx} setIndex={setLbIdx} store={store} role={role}
          project={project} onClose={() => setLbIdx(-1)} />
      )}

      {compareMode && compareSel.length === 2 && (
        <CompareLauncher ids={compareSel} list={all} store={store} role={role}
          onClose={() => setCompareSel([])} />
      )}
    </div>
  );
}

window.Gallery = Gallery;
