import { useMemo, useState } from 'react';
import { STICKERS, STICKER_CATEGORIES } from '../../constants/stickers';
import '../../styles/StickersTab.css';

/**
 * Stickers tab — image stickers from `public/stickers/`.
 *
 * Lists every PNG/WebP/JPG/SVG dropped into the public/stickers/ folder,
 * grouped by the category prefix in its filename. Tapping a sticker adds it
 * to the canvas as a 'sticker'-type element with the file's URL as the src.
 *
 * Image fetches are lazy: each thumbnail uses `<img loading="lazy">`, so a
 * library of 200+ stickers doesn't fire 200 GETs on tab open. The browser
 * pulls them in as the user scrolls through the grid.
 *
 * Adding stickers is folder-driven (see public/stickers/README.md). There's
 * no constants file to edit and no manifest to regenerate — a small Vite
 * plugin reads the directory at build time and emits the file list.
 *
 * The Emoji tab (EmojiTab.jsx) used to live here too as a `variant="emoji"`
 * mode. It's a separate component now: image stickers and emoji glyphs have
 * essentially no shared rendering path beyond the grid CSS, which both reuse.
 */
export function StickersTab({ onAddSticker }) {
  const [activeCategory, setActiveCategory] = useState('all');
  const [failedIds, setFailedIds] = useState(() => new Set());

  // Categories pill bar. Prepend an "All" entry that the data layer
  // intentionally doesn't include — "All" is a UI concept (filter
  // disable), not a data category.
  const categoryPills = useMemo(
    () => [{ id: 'all', label: 'All' }, ...STICKER_CATEGORIES],
    [],
  );

  const visibleStickers = useMemo(() => {
    if (activeCategory === 'all') return STICKERS;
    return STICKERS.filter((s) => s.categoryId === activeCategory);
  }, [activeCategory]);

  const handleAddSticker = async (sticker) => {
    // Read the source image's natural dimensions so the sticker lands on
    // the canvas with its true aspect ratio, not as a stretched square.
    // The thumbnail's <img> in the panel may not have loaded yet (it's
    // lazy) and even if it has, the DOM element isn't a stable place to
    // pull `naturalWidth` from — we just construct a fresh Image. If the
    // browser already has the file in cache the onload fires immediately;
    // otherwise we wait for the network. Either way, the click feels
    // responsive because the bottleneck is decode latency, not network.
    //
    // Falls back to a square 80×80 if the load fails for any reason
    // (broken file, CORS oddity, blocked). That's strictly no worse than
    // the previous unconditional 80×80 placement.
    const fitInto = 80; // design-coord bounding box
    let width = fitInto;
    let height = fitInto;
    try {
      const dims = await new Promise((resolveDims, rejectDims) => {
        const img = new Image();
        img.onload = () => resolveDims({ w: img.naturalWidth, h: img.naturalHeight });
        img.onerror = () => rejectDims(new Error('Sticker image failed to load'));
        img.src = sticker.url;
      });
      if (dims.w > 0 && dims.h > 0) {
        // Fit (dims.w × dims.h) into an 80×80 box, preserving aspect.
        // The longer side becomes 80; the shorter side scales
        // proportionally. Equal sides land on 80×80 (matches old
        // behavior for square sources).
        const scale = fitInto / Math.max(dims.w, dims.h);
        width = dims.w * scale;
        height = dims.h * scale;
      }
    } catch {
      // Keep the 80×80 fallback. handleImgError will fire from the
      // thumbnail render path separately if the file is truly missing.
    }

    // Center the (width, height) box on the print zone center. Design
    // coords are 0–300, so center is at 150. With width=height=80, this
    // produces (x: 110, y: 110), matching the old fixed placement — so
    // square stickers don't appear to move relative to the previous
    // behavior. Non-square stickers center properly instead of having
    // their top-left pinned (which would have skewed their visual
    // center off the print-zone center).
    const printCenter = 150;
    onAddSticker({
      type: 'sticker',
      x: printCenter - width / 2,
      y: printCenter - height / 2,
      width,
      height,
      rotation: 0,
      src: sticker.url,
    });
  };

  const handleImgError = (id) => {
    // Track failed loads so we can hide broken thumbnails. We don't
    // remove the sticker entry — the file is presumably there (the
    // manifest came from a directory listing), so a 404 likely means a
    // dev-server hiccup that'll fix itself on reload. Hiding the broken
    // image tile is just polite.
    setFailedIds((prev) => {
      if (prev.has(id)) return prev;
      const next = new Set(prev);
      next.add(id);
      return next;
    });
  };

  // Empty-state: no stickers in the folder. Friendlier than rendering an
  // empty pill bar and a blank grid. The README at public/stickers/README.md
  // explains the filename convention; pointing the user there in the
  // message would be over-explaining for an end-user-facing surface — this
  // copy is intentionally generic.
  if (STICKERS.length === 0) {
    return (
      <div className="st st--full">
        <div className="st__header">
          <h3 className="st__title">Stickers Library</h3>
        </div>
        <p className="st__empty">
          No stickers available yet. Stickers will appear here once they're
          added to the library.
        </p>
      </div>
    );
  }

  return (
    <div className="st st--full">
      <div className="st__header">
        <h3 className="st__title">Stickers Library</h3>
      </div>

      <div className="st__categories" role="tablist" aria-label="Sticker categories">
        {categoryPills.map((cat) => {
          const isActive = cat.id === activeCategory;
          return (
            <button
              key={cat.id}
              type="button"
              role="tab"
              aria-selected={isActive}
              className={`st__category${isActive ? ' is-active' : ''}`}
              onClick={() => setActiveCategory(cat.id)}
            >
              {cat.label}
            </button>
          );
        })}
      </div>

      <div className="st__grid">
        {visibleStickers.map((sticker) => {
          if (failedIds.has(sticker.id)) return null;
          return (
            <button
              key={sticker.id}
              type="button"
              onClick={() => handleAddSticker(sticker)}
              className="st__sticker st__sticker--image"
              aria-label={`Add sticker ${sticker.name}`}
              title={sticker.name}
            >
              <img
                src={sticker.url}
                alt=""
                loading="lazy"
                decoding="async"
                className="st__sticker-img"
                onError={() => handleImgError(sticker.id)}
              />
            </button>
          );
        })}
      </div>
    </div>
  );
}
