// Photo Gallery — masonry + lightbox
// Photos are loaded from /api/photos (D1+R2). Guests upload via the QR card or the picker.

// --- Upload helper -------------------------------------------------
// 1. POST /api/upload-url to register the file and get a per-photo PUT URL.
// 2. PUT the file bytes directly to that URL.
async function uploadPhoto(file, { caption = '', by = 'Anonymous' } = {}) {
  const sign = await fetch('/api/upload-url', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      filename: file.name,
      contentType: file.type,
      size: file.size,
      caption,
      by,
    }),
  }).then((r) => r.json());
  if (!sign.ok) throw new Error(sign.error || 'Could not get upload URL.');

  const put = await fetch(sign.uploadUrl, {
    method: 'PUT',
    headers: { 'content-type': file.type },
    body: file,
  });
  if (!put.ok) throw new Error('Upload failed.');
  return sign.key;
}

// Real, scannable QR code — uses qrcode-generator (loaded in head)
const RealQR = ({ url }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!ref.current || typeof window.qrcode !== 'function') return;
    const qr = window.qrcode(0, 'M');
    qr.addData(url);
    qr.make();
    const count = qr.getModuleCount();
    const cell = 4;
    const size = count * cell;
    let path = '';
    for (let r = 0; r < count; r++) {
      for (let c = 0; c < count; c++) {
        if (qr.isDark(r, c)) {
          path += `M${c * cell},${r * cell}h${cell}v${cell}h-${cell}z`;
        }
      }
    }
    ref.current.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" width="120" height="120" shape-rendering="crispEdges"><path d="${path}" fill="#2a2a2a"/></svg>`;
  }, [url]);
  return (
    <div className="qr-card">
      <a href={url} target="_blank" rel="noopener noreferrer" className="qr-code-real" aria-label={`Open ${url}`}>
        <span ref={ref} />
      </a>
      <div className="qr-info">
        <span className="eyebrow">Scan to contribute</span>
        <h3 className="serif" style={{ fontSize: 22, fontStyle: 'italic', margin: '4px 0' }}>
          Add your photos & videos
        </h3>
        <p style={{ fontSize: 12, color: 'var(--ink-soft)', margin: 0, lineHeight: 1.5 }}>
          Or visit <a href={url} target="_blank" rel="noopener noreferrer" style={{ color: 'var(--accent)', textDecoration: 'underline' }}>shanthi-and-nick.ca</a> — uploads appear here after the celebration.
        </p>
      </div>
    </div>
  );
};

const FILTERS = [
  { id: 'all', label: 'Everything' },
  { id: 'pre', label: 'Pre-Wedding' },
  { id: 'cer', label: 'Ceremony' },
  { id: 'rec', label: 'Reception' },
];

const Gallery = () => {
  const [photos, setPhotos] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [open, setOpen] = React.useState(null);
  const [filter, setFilter] = React.useState('all');
  const [uploadState, setUploadState] = React.useState({ kind: 'idle', msg: '' });
  const fileRef = React.useRef(null);

  // Load photos from D1 on mount.
  React.useEffect(() => {
    let cancelled = false;
    fetch('/api/photos')
      .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`))))
      .then((p) => {
        if (cancelled) return;
        setPhotos((p.photos || []).map((ph, i) => ({
          id: ph.id,
          src: ph.src,
          cap: ph.caption || '',
          date: (ph.created_at || '').slice(0, 10),
          category: ph.category || 'all',
        })));
        setLoading(false);
      })
      .catch(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, []);

  const filtered = filter === 'all' ? photos : photos.filter((p) => p.category === filter);

  const onPickFiles = async (e) => {
    const files = Array.from(e.target.files || []);
    if (!files.length) return;
    setUploadState({ kind: 'busy', msg: `Uploading ${files.length} file${files.length === 1 ? '' : 's'}…` });
    try {
      for (const f of files) {
        await uploadPhoto(f, { caption: '', by: 'Guest' });
      }
      setUploadState({
        kind: 'ok',
        msg: 'Received! Photos appear here after the couple approves them.',
      });
    } catch (err) {
      setUploadState({ kind: 'err', msg: err.message || 'Upload failed.' });
    } finally {
      if (fileRef.current) fileRef.current.value = '';
    }
  };

  const idx = open !== null ? filtered.findIndex((p) => p.id === open) : -1;
  const next = () => filtered.length && setOpen(filtered[(idx + 1) % filtered.length].id);
  const prev = () => filtered.length && setOpen(filtered[(idx - 1 + filtered.length) % filtered.length].id);

  React.useEffect(() => {
    if (open === null) return;
    const onKey = (e) => {
      if (e.key === 'Escape') setOpen(null);
      if (e.key === 'ArrowRight') next();
      if (e.key === 'ArrowLeft') prev();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, idx]);

  return (
    <section className="section gallery-section" id="gallery">
      <div className="section-head">
        <div>
          <div className="eyebrow" style={{ marginBottom: 12 }}>07 — Gallery</div>
          <h2 className="title">Three days, in <em>frames</em></h2>
        </div>
        <div className="meta">
          A shared album, after the wedding
        </div>
      </div>

      <div className="gallery-top">
        <div className="recipe-filters" style={{ margin: 0 }}>
          {FILTERS.map(f => (
            <button key={f.id} className={`filter-chip ${filter === f.id ? 'on' : ''}`} onClick={() => setFilter(f.id)}>
              {f.label}
            </button>
          ))}
        </div>

        <RealQR url="https://shanthi-and-nick.ca" />
      </div>

      {/* Upload — visible on the device too, not just via QR */}
      <div className="upload-row glass" style={{
        display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between',
        gap: 16, padding: '18px 24px', borderRadius: 16, margin: '0 0 28px',
      }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="eyebrow">Contribute</div>
          <div className="serif" style={{ fontSize: 20, fontStyle: 'italic', marginTop: 4 }}>
            Have a photo from the day? Drop it in.
          </div>
          <p style={{ fontSize: 12, color: 'var(--ink-soft)', margin: '4px 0 0', lineHeight: 1.5 }}>
            JPG, PNG, HEIC, MP4 — up to 25 MB each. Uploads appear in the gallery right away.
          </p>
        </div>
        <label className="btn" style={{ cursor: 'pointer', whiteSpace: 'nowrap' }}>
          {uploadState.kind === 'busy' ? 'Uploading…' : 'Choose files →'}
          <input
            ref={fileRef}
            type="file"
            accept="image/*,video/mp4,video/quicktime"
            multiple
            onChange={onPickFiles}
            disabled={uploadState.kind === 'busy'}
            style={{ display: 'none' }}
          />
        </label>
        {uploadState.kind !== 'idle' && (
          <div style={{
            flexBasis: '100%',
            fontFamily: 'var(--mono)', fontSize: 11, letterSpacing: '0.06em',
            color: uploadState.kind === 'err' ? '#b13b3b'
                 : uploadState.kind === 'ok' ? 'var(--accent)'
                 : 'var(--ink-soft)',
          }}>
            {uploadState.msg}
          </div>
        )}
      </div>

      {loading ? (
        <div className="gallery-empty">
          <div className="gallery-empty-mark mono">Loading album…</div>
        </div>
      ) : filtered.length === 0 ? (
        <div className="gallery-empty">
          <div className="gallery-empty-mark mono">28 · 08 · 26</div>
          <h3 className="serif" style={{ fontSize: 32, fontStyle: 'italic', margin: '12px 0 8px', color: 'var(--ink)' }}>
            The album opens after the celebration
          </h3>
          <p style={{ fontSize: 14, color: 'var(--ink-soft)', maxWidth: 480, margin: '0 auto', lineHeight: 1.6 }}>
            Engagement frames, ceremony, and the reception will live here. Until then —
            scan the code above, contribute your own, and we'll see you in Patras.
          </p>
        </div>
      ) : (
        <div className="masonry">
          {filtered.map((p, i) => (
            <figure key={p.id} className={`masonry-tile ${p.tall ? 'tall' : ''}`} onClick={() => setOpen(p.id)}>
              <img src={p.src} alt={p.cap} loading="lazy" />
              <figcaption className="masonry-cap">
                <span className="masonry-num mono">№ {String(i + 1).padStart(2, '0')}</span>
                <span className="masonry-text">{p.cap}</span>
              </figcaption>
            </figure>
          ))}
        </div>
      )}

      {open !== null && filtered.length > 0 && (
        <div className="lightbox" onClick={() => setOpen(null)}>
          <button className="lb-close" onClick={() => setOpen(null)}>×</button>
          <button className="lb-nav lb-prev" onClick={(e) => { e.stopPropagation(); prev(); }}>←</button>
          <button className="lb-nav lb-next" onClick={(e) => { e.stopPropagation(); next(); }}>→</button>

          <div className="lb-stage" onClick={e => e.stopPropagation()}>
            <img src={filtered[idx].src} alt={filtered[idx].cap} />
            <div className="lb-meta">
              <div>
                <span className="eyebrow" style={{ color: 'rgba(255,255,255,0.6)' }}>{filtered[idx].date}</span>
                <h3 className="serif" style={{ fontSize: 32, color: 'white', fontStyle: 'italic', marginTop: 4 }}>{filtered[idx].cap}</h3>
              </div>
              <span className="mono" style={{ color: 'rgba(255,255,255,0.6)', fontSize: 11, letterSpacing: '0.16em' }}>
                {idx + 1} / {filtered.length}
              </span>
            </div>
          </div>
        </div>
      )}
    </section>
  );
};

window.Gallery = Gallery;
