// Objects — a curated, Supabase-backed shelf of things I like.
//
// Data lives in the `objects` table (see 20260608100000_objects.sql). The tile
// image prefers a high-res override uploaded to the `object-images` bucket and
// falls back to the stock image scraped from the source link, then to a clean
// placeholder. The owner can add objects by pasting a link (auto-classified by
// the ingest-object function), editing fields, and optionally uploading a
// higher-res image — or email/Instagram a link for hands-off auto-upload.

// Image with graceful fallback: override → online → placeholder.
const ObjectImage = ({ obj }) => {
  const sources = [obj.image_override, obj.image_url].filter(Boolean);
  const [idx, setIdx] = React.useState(0);
  const src = sources[idx];
  if (!src) {
    return (
      <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${tokens.bgAlt}, ${tokens.paper})`, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <span style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.16em', color: tokens.inkMute, textTransform: 'uppercase' }}>{obj.category || 'Object'}</span>
      </div>
    );
  }
  return (
    <img
      src={src}
      alt={obj.title}
      loading="lazy"
      onError={() => setIdx((i) => i + 1)}
      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
    />
  );
};

// ── Owner add/edit form ─────────────────────────────────────────────────────
const FIELD = { width: '100%', padding: '10px 12px', border: `1px solid ${tokens.inkLine}`, background: tokens.paper, fontSize: '13px', fontFamily: fontDisplay, marginTop: '4px' };
const LABEL = { fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.12em', color: tokens.inkMute, textTransform: 'uppercase' };

const ObjectForm = ({ initial, onSaved, onCancel }) => {
  const blank = { title: '', maker: '', category: 'Goods', subcategory: '', description: '', source_url: '', affiliate_url: '', merchant: '', image_url: '', image_override: '', price: '', year: '', location: '', tags: [], size: 'md', featured: false, status: 'published', sort_order: 0, attributes: {} };
  const [o, setO] = React.useState({ ...blank, ...(initial || {}) });
  const [busy, setBusy] = React.useState('');
  const [err, setErr] = React.useState('');
  const set = (k, v) => setO((prev) => ({ ...prev, [k]: v }));
  const client = window._supabaseClient;

  // Paste a link → ask the function to scrape + classify, then fill the form.
  const autofill = async () => {
    if (!o.source_url) { setErr('Paste a link first.'); return; }
    setErr(''); setBusy('autofill');
    try {
      const { data, error } = await client.functions.invoke('ingest-object', { body: { url: o.source_url, preview: true } });
      let payload = data;
      if (error) { try { payload = await error.context.json(); } catch {} }
      if (!payload || payload.error) throw new Error(payload?.error || error?.message || 'Auto-fill failed');
      const p = payload.object || {};
      setO((prev) => ({ ...prev, ...p, source_url: o.source_url, tags: p.tags || [], attributes: p.attributes || {} }));
    } catch (e) { setErr(e.message); } finally { setBusy(''); }
  };

  // Upload a high-res override to the object-images bucket.
  const upload = async (file) => {
    if (!file) return;
    setErr(''); setBusy('upload');
    try {
      const ext = (file.name.split('.').pop() || 'jpg').toLowerCase();
      const path = `${(o.title || 'object').toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 40)}-${Date.now()}.${ext}`;
      const { error } = await client.storage.from('object-images').upload(path, file, { upsert: true, contentType: file.type });
      if (error) throw error;
      const { data } = client.storage.from('object-images').getPublicUrl(path);
      set('image_override', data.publicUrl);
    } catch (e) { setErr(e.message); } finally { setBusy(''); }
  };

  const save = async () => {
    if (!o.title) { setErr('Title is required.'); return; }
    setErr(''); setBusy('save');
    const row = { ...o, tags: Array.isArray(o.tags) ? o.tags : String(o.tags).split(',').map((s) => s.trim()).filter(Boolean), updated_at: new Date().toISOString() };
    // Never write immutable / server-managed columns.
    delete row.id; delete row.created_at; delete row.user_id;
    try {
      let res;
      if (o.id) res = await client.from('objects').update(row).eq('id', o.id);
      else res = await client.from('objects').insert(row);
      if (res.error) throw res.error;
      onSaved();
    } catch (e) { setErr(e.message); } finally { setBusy(''); }
  };

  const tagsStr = Array.isArray(o.tags) ? o.tags.join(', ') : (o.tags || '');

  return (
    <div style={{ border: `1px solid ${tokens.ink}`, background: tokens.paper, padding: '24px', marginBottom: '24px', borderRadius: '4px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '18px' }}>
        <div style={{ fontFamily: fontMono, fontSize: '12px', letterSpacing: '0.14em', textTransform: 'uppercase' }}>{o.id ? 'Edit object' : 'Add object'}</div>
        <button onClick={onCancel} style={{ ...LABEL, background: 'none', border: 'none', cursor: 'pointer' }}>CLOSE ✕</button>
      </div>

      {/* link + autofill */}
      <div style={{ display: 'flex', gap: '8px', alignItems: 'flex-end', marginBottom: '16px' }}>
        <div style={{ flexGrow: 1 }}>
          <span style={LABEL}>Source link (paste an article, maker page, or Instagram URL)</span>
          <input value={o.source_url || ''} onChange={(e) => set('source_url', e.target.value)} placeholder="https://…" style={FIELD} />
        </div>
        <button onClick={autofill} disabled={!!busy} style={{ padding: '10px 16px', background: tokens.ink, color: tokens.bg, border: 'none', cursor: 'pointer', fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.08em', whiteSpace: 'nowrap' }}>
          {busy === 'autofill' ? 'READING…' : '✦ AUTO-FILL'}
        </button>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px 16px' }}>
        <label><span style={LABEL}>Title</span><input value={o.title} onChange={(e) => set('title', e.target.value)} style={FIELD} /></label>
        <label><span style={LABEL}>Maker / Brand</span><input value={o.maker || ''} onChange={(e) => set('maker', e.target.value)} style={FIELD} /></label>
        <label><span style={LABEL}>Category</span><input value={o.category} onChange={(e) => set('category', e.target.value)} placeholder="Automobiles · Watches · Hotels…" style={FIELD} /></label>
        <label><span style={LABEL}>Subcategory</span><input value={o.subcategory || ''} onChange={(e) => set('subcategory', e.target.value)} style={FIELD} /></label>
        <label style={{ gridColumn: 'span 2' }}><span style={LABEL}>Description</span><textarea value={o.description || ''} onChange={(e) => set('description', e.target.value)} rows={2} style={{ ...FIELD, resize: 'vertical' }} /></label>
        <label><span style={LABEL}>Affiliate link (optional)</span><input value={o.affiliate_url || ''} onChange={(e) => set('affiliate_url', e.target.value)} placeholder="monetization URL" style={FIELD} /></label>
        <label><span style={LABEL}>Merchant</span><input value={o.merchant || ''} onChange={(e) => set('merchant', e.target.value)} style={FIELD} /></label>
        <label><span style={LABEL}>Price</span><input value={o.price || ''} onChange={(e) => set('price', e.target.value)} placeholder="$280 / POA" style={FIELD} /></label>
        <label><span style={LABEL}>Year</span><input value={o.year || ''} onChange={(e) => set('year', e.target.value)} style={FIELD} /></label>
        <label style={{ gridColumn: 'span 2' }}><span style={LABEL}>Tags (comma-separated)</span><input value={tagsStr} onChange={(e) => set('tags', e.target.value.split(',').map((s) => s.trim()).filter(Boolean))} style={FIELD} /></label>
        <label><span style={LABEL}>Stock image URL</span><input value={o.image_url || ''} onChange={(e) => set('image_url', e.target.value)} style={FIELD} /></label>
        <label><span style={LABEL}>Tile size</span>
          <select value={o.size} onChange={(e) => set('size', e.target.value)} style={FIELD}>
            <option value="sm">Small</option><option value="md">Medium</option><option value="lg">Large (hero)</option>
          </select>
        </label>
        <label style={{ gridColumn: 'span 2' }}>
          <span style={LABEL}>High-res override image {o.image_override ? '✓ set' : '(upload to override the stock image)'}</span>
          <input type="file" accept="image/*" onChange={(e) => upload(e.target.files[0])} style={{ ...FIELD, padding: '8px' }} />
        </label>
      </div>

      {err && <div style={{ marginTop: '12px', fontFamily: fontMono, fontSize: '11px', color: tokens.red }}>{err}</div>}

      <div style={{ display: 'flex', gap: '10px', marginTop: '18px', alignItems: 'center' }}>
        <button onClick={save} disabled={!!busy} style={{ padding: '11px 20px', background: tokens.ink, color: tokens.bg, border: 'none', cursor: 'pointer', fontSize: '13px', fontFamily: fontDisplay }}>
          {busy === 'save' ? 'Saving…' : (o.id ? 'Update object' : 'Add object')}
        </button>
        <label style={{ ...LABEL, display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer' }}>
          <input type="checkbox" checked={!!o.featured} onChange={(e) => set('featured', e.target.checked)} /> FEATURED
        </label>
        {busy === 'upload' && <span style={{ ...LABEL }}>UPLOADING IMAGE…</span>}
      </div>
    </div>
  );
};

const Objects = ({ isOwner }) => {
  const isMobile = useIsMobile();
  const [objects, setObjects] = React.useState(null);
  const [filter, setFilter] = React.useState('all');
  const [editing, setEditing] = React.useState(null); // null | {} | row
  const [loadErr, setLoadErr] = React.useState('');

  const load = React.useCallback(async () => {
    const client = window._supabaseClient;
    if (!client) { setObjects([]); return; }
    const { data, error } = await client.from('objects').select('*').order('featured', { ascending: false }).order('sort_order', { ascending: false }).order('created_at', { ascending: false });
    if (error) { setLoadErr(error.message); setObjects([]); }
    else setObjects(data || []);
  }, []);

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

  const cats = React.useMemo(() => {
    const set = new Set((objects || []).map((o) => o.category).filter(Boolean));
    return ['all', ...Array.from(set)];
  }, [objects]);

  const filtered = (objects || []).filter((o) => filter === 'all' || o.category === filter);

  const span = (s) => {
    // On mobile the grid is 2 columns: hero/medium tiles go full-width, small tiles half.
    if (isMobile) return (s === 'lg' || s === 'md') ? { gridColumn: 'span 2', aspectRatio: '2 / 1' } : { gridColumn: 'span 1', aspectRatio: '1' };
    if (s === 'lg') return { gridColumn: 'span 2', gridRow: 'span 2', aspectRatio: '1' };
    if (s === 'md') return { gridColumn: 'span 2', gridRow: 'span 1', aspectRatio: '2 / 1' };
    return { gridColumn: 'span 1', gridRow: 'span 1', aspectRatio: '1' };
  };

  const del = async (id) => {
    if (!roleCanDelete()) { window.alert(DEMO_BLOCK_MSG); return; }
    if (!window.confirm('Delete this object?')) return;
    await window._supabaseClient.from('objects').delete().eq('id', id);
    load();
  };

  return (
    <div>
      {/* Hero */}
      <section style={{ padding: '80px 56px 40px', borderBottom: `1px solid ${tokens.inkLine}` }}>
        <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.18em', color: tokens.inkMute, marginBottom: '24px' }}>06 · OBJECTS</div>
        <h1 style={{ fontSize: '88px', lineHeight: 0.92, letterSpacing: '-0.05em', fontWeight: 400, margin: 0, marginBottom: '32px' }}>Objects.</h1>
        <p style={{ fontSize: '22px', lineHeight: 1.45, letterSpacing: '-0.015em', color: tokens.inkSoft, maxWidth: '780px' }}>
          A curated shelf of things I find well-made or worth coveting — cars, watches, places, and the occasional small good. Some link out to the maker; a few may carry affiliate links, always disclosed.
        </p>
      </section>

      {/* Filter bar */}
      <section style={{ padding: '20px 56px', borderBottom: `1px solid ${tokens.inkLine}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '16px', flexWrap: 'wrap', position: 'sticky', top: isMobile ? 0 : 56, zIndex: 20, background: 'rgba(244,241,234,0.92)', backdropFilter: 'blur(10px)' }}>
        <div style={{ display: 'flex', gap: '20px', fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.12em', flexWrap: 'wrap' }}>
          {cats.map((c) => (
            <button key={c} onClick={() => setFilter(c)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: filter === c ? tokens.ink : tokens.inkMute, borderBottom: filter === c ? `1px solid ${tokens.ink}` : '1px solid transparent', padding: '6px 0', letterSpacing: '0.12em', textTransform: 'uppercase' }}>{c}</button>
          ))}
        </div>
        {isOwner && (
          <button onClick={() => setEditing(editing ? null : {})} style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', color: tokens.bg, background: tokens.ink, border: 'none', padding: '8px 14px', cursor: 'pointer' }}>
            {editing ? '✕ CLOSE' : '＋ ADD OBJECT'}
          </button>
        )}
      </section>

      {/* Owner form */}
      {isOwner && editing && (
        <section style={{ padding: isMobile ? '20px 18px 0' : '24px 56px 0' }}>
          <ObjectForm initial={editing.id ? editing : {}} onSaved={() => { setEditing(null); load(); }} onCancel={() => setEditing(null)} />
        </section>
      )}

      {/* Grid */}
      <section style={{ padding: isMobile ? '12px' : '24px' }}>
        {objects === null ? (
          <div style={{ padding: '80px', textAlign: 'center', fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.16em', color: tokens.inkMute }}>LOADING…</div>
        ) : filtered.length === 0 ? (
          <div style={{ padding: '80px', textAlign: 'center', fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.12em', color: tokens.inkMute }}>
            {loadErr ? `COULDN'T LOAD — ${loadErr.toUpperCase()}` : 'NO OBJECTS YET'}
          </div>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: isMobile ? 'repeat(2, minmax(0, 1fr))' : 'repeat(4, minmax(0, 1fr))', gridAutoRows: isMobile ? 'minmax(150px, auto)' : 'minmax(220px, auto)', gap: '4px' }}>
            {filtered.map((o) => {
              const href = o.affiliate_url || o.source_url || null;
              return (
                <a key={o.id} href={href || undefined} target={href ? '_blank' : undefined} rel="noopener noreferrer" className="object-tile" style={{ ...span(o.size), position: 'relative', background: tokens.paper, overflow: 'hidden', cursor: href ? 'pointer' : 'default', display: 'block' }}>
                  <ObjectImage obj={o} />

                  {/* category badge */}
                  <div style={{ position: 'absolute', top: '14px', left: '14px', fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.16em', padding: '5px 8px', background: 'rgba(251,250,246,0.92)', color: tokens.ink, fontWeight: 500, textTransform: 'uppercase' }}>
                    {o.category}{o.affiliate_url ? ' · AFFILIATE' : ''}
                  </div>

                  {/* owner controls */}
                  {isOwner && (
                    <div style={{ position: 'absolute', top: '12px', right: '12px', display: 'flex', gap: '6px', zIndex: 3 }}>
                      <button onClick={(e) => { e.preventDefault(); setEditing(o); window.scrollTo({ top: 0, behavior: 'smooth' }); }} style={{ width: '26px', height: '26px', borderRadius: '50%', border: 'none', background: 'rgba(251,250,246,0.95)', cursor: 'pointer', fontSize: '11px' }}>✎</button>
                      {roleCanDelete() && <button onClick={(e) => { e.preventDefault(); del(o.id); }} style={{ width: '26px', height: '26px', borderRadius: '50%', border: 'none', background: 'rgba(160,74,62,0.95)', color: '#fff', cursor: 'pointer', fontSize: '11px' }}>✕</button>}
                    </div>
                  )}

                  {/* caption */}
                  <div className="object-caption" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, padding: '18px', background: 'linear-gradient(to top, rgba(14,14,12,0.92) 0%, rgba(14,14,12,0.65) 55%, transparent 100%)', color: tokens.bg }}>
                    {(o.subcategory || o.year) && (
                      <div style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.16em', color: 'rgba(244,241,234,0.6)', marginBottom: '6px', textTransform: 'uppercase' }}>
                        {[o.year, o.subcategory].filter(Boolean).join(' · ')}
                      </div>
                    )}
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: '12px' }}>
                      <div>
                        <div style={{ fontSize: o.size === 'lg' ? '20px' : '15px', letterSpacing: '-0.02em', lineHeight: 1.2, marginBottom: '4px' }}>{o.title}</div>
                        {o.maker && <div style={{ fontFamily: fontMono, fontSize: '10px', color: 'rgba(244,241,234,0.6)', letterSpacing: '0.06em' }}>{o.maker.toUpperCase()}</div>}
                      </div>
                      {o.price && <div style={{ fontFamily: fontDisplay, fontSize: o.size === 'lg' ? '22px' : '16px', letterSpacing: '-0.02em', whiteSpace: 'nowrap' }}>{o.price}</div>}
                    </div>
                  </div>

                  {href && (
                    <div className="object-arrow" style={{ position: 'absolute', bottom: '14px', right: '14px', width: '28px', height: '28px', background: 'rgba(251,250,246,0.92)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: fontMono, fontSize: '12px', color: tokens.ink }}>↗</div>
                  )}
                </a>
              );
            })}
          </div>
        )}
      </section>

      {/* Footer note */}
      <section style={{ padding: '60px 56px', borderTop: `1px solid ${tokens.inkLine}`, background: tokens.bgAlt }}>
        <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.16em', color: tokens.inkMute, marginBottom: '16px' }}>ON THIS SHELF</div>
        <p style={{ fontSize: '15px', lineHeight: 1.6, color: tokens.inkSoft, letterSpacing: '-0.005em', maxWidth: '560px', margin: 0 }}>
          A living collection — added to over time, sometimes by emailing myself a link. Objects are classified as they're added; affiliate links, where present, are disclosed and never the reason something is here.
        </p>
      </section>
    </div>
  );
};

window.Objects = Objects;
