// studio-brands.jsx — the admin screen behind Studio → Brands.
//
// Create, edit and delete rows in the `brands` table. Each row is a page at
// /brands/<slug>, so saving here is publishing: no code, no deploy.
//
// The screen is only offered to admins, and that is only a courtesy — the
// policies on `brands` refuse a write from anyone whose profile doesn't carry
// is_admin, whatever the browser does. The same flag guards the brand-images
// bucket, so the people who can change a brand page are the people who can
// change its picture.
//
// A hero image goes through the same background removal product shots do. The
// cutout is stored beside the original rather than replacing it: it is derived,
// and a derived thing must never be the only copy.

const { useState: useStateAB, useEffect: useEffectAB } = React;

const EMPTY_BRAND = {
  id: null, slug: '', name: '', category: '', estYear: '',
  location: '', story: '', platformNote: '', tags: '', heroImageUrl: '',
  heroImageTransparentUrl: '', removeBg: true, file: null, filePreview: null,
};

function StudioBrands({ isAdmin, go }) {
  const [brands, setBrands] = useStateAB([]);
  const [loading, setLoading] = useStateAB(true);
  const [editing, setEditing] = useStateAB(null);     // a draft, or null
  const [busy, setBusy] = useStateAB(false);
  const [confirmId, setConfirmId] = useStateAB(null);
  const [msg, setMsg] = useStateAB(null);
  const [step, setStep] = useStateAB(null);       // what the save is doing right now
  const [baking, setBaking] = useStateAB(null);   // progress of the bulk cutout pass

  const load = async () => {
    const res = await window.MBBrands.list();
    if (res.error) setMsg({ kind: 'error', text: res.error });
    else setBrands(res.brands);
    setLoading(false);
  };

  useEffectAB(() => { load(); }, []);

  const flash = (kind, text) => {
    setMsg({ kind, text });
    setTimeout(() => setMsg(null), 3600);
  };

  const startNew = () => setEditing({ ...EMPTY_BRAND });
  const startEdit = (b) => setEditing({
    id: b.id, slug: b.slug, name: b.name, category: b.category,
    estYear: b.estYear == null ? '' : String(b.estYear),
    location: b.location, story: b.story, platformNote: b.platformNote,
    tags: (b.tags || []).join(', '), heroImageUrl: b.heroImageUrl || '',
    heroImageTransparentUrl: b.heroImageTransparentUrl || '',
    // Editing a brand that already has a cutout keeps making one; an admin who
    // wants the raw picture turns it off and the stored cutout is dropped.
    removeBg: true, file: null, filePreview: null,
  });

  // Save is three steps, and only the last one is the row: upload the picture
  // if one was chosen, cut its background out, then write both URLs. A cutout
  // that can't be made is a note, never a blocked save — the brand page falls
  // back to the picture itself.
  const save = async () => {
    if (busy || !editing) return;
    setBusy(true);
    const draft = { ...editing };
    const slug = window.MBBrands.slugify(draft.slug || draft.name);
    let note = '';

    if (draft.file) {
      setStep('Uploading the image…');
      const up = await window.MBBrands.uploadHeroSource(slug, draft.file);
      if (up.error) { setBusy(false); setStep(null); flash('error', up.error); return; }
      draft.heroImageUrl = up.url;
    }

    if (draft.removeBg && draft.heroImageUrl) {
      setStep('Removing the background…');
      // Cut from the file in hand when there is one: a blob: URL is same-origin,
      // so the canvas can read it without a round trip through an image proxy.
      const local = draft.file ? URL.createObjectURL(draft.file) : null;
      const cut = await window.MBBrands.processHeroImage(slug, local || draft.heroImageUrl);
      if (local) URL.revokeObjectURL(local);
      if (cut.error) { setBusy(false); setStep(null); flash('error', cut.error); return; }
      draft.heroImageTransparentUrl = cut.url || null;
      if (cut.skipped) note = ` The background was left alone — ${cut.skipped}.`;
    } else {
      draft.heroImageTransparentUrl = null;
    }

    setStep('Saving…');
    const res = await window.MBBrands.save(draft);
    setBusy(false);
    setStep(null);
    if (res.error) { flash('error', res.error); return; }
    setEditing(null);
    await load();
    flash(note ? 'warn' : 'ok', `Saved. The page is live at modaboard.com/brands/${res.brand.slug}.` + note);
  };

  // Brands whose picture still carries its own background. The page cuts one
  // out on the fly so they never look wrong, but doing it once here and storing
  // the result means every visitor after this stops paying for it.
  const pending = brands.filter(b => b.heroImageUrl && !b.heroImageTransparentUrl);

  const bakeAll = async () => {
    if (baking) return;
    const queue = pending.slice();
    const failed = [];
    let done = 0;
    for (const b of queue) {
      setBaking({ done, total: queue.length, name: b.name });
      const cut = await window.MBBrands.processHeroImage(b.slug, b.heroImageUrl);
      if (cut.url) {
        const saved = await window.MBBrands.setHeroTransparent(b.id, cut.url);
        if (saved.error) failed.push(`${b.name}: ${saved.error}`);
      } else {
        failed.push(`${b.name}: ${cut.error || cut.skipped}`);
      }
      done++;
    }
    setBaking(null);
    await load();
    if (failed.length) flash('warn', `${queue.length - failed.length} of ${queue.length} done. ${failed.join(' · ')}`);
    else flash('ok', `${queue.length} brand image${queue.length === 1 ? '' : 's'} now render on a transparent background.`);
  };

  const remove = async (b) => {
    if (busy) return;
    setBusy(true);
    const res = await window.MBBrands.remove(b.id);
    setBusy(false);
    setConfirmId(null);
    if (res.error) { flash('error', res.error); return; }
    await load();
    flash('ok', `${b.name} deleted — its page is gone too.`);
  };

  if (!isAdmin) {
    return (
      <div>
        <StudioHeader kicker="Studio · Brands" title="Admins only."/>
        <Card>
          <div style={{ fontSize: 14, color: '#5a5a54', lineHeight: 1.6, maxWidth: '52ch' }}>
            Brand pages are edited by modaBoard admins. Your account doesn’t have that flag,
            so there’s nothing to do here — the brand pages themselves are public and
            readable by everyone.
          </div>
          <button onClick={() => go && go({ name: 'brands' })} style={{ ...brandBtnGhost, marginTop: 16 }}>
            View the brand directory
          </button>
        </Card>
      </div>
    );
  }

  return (
    <div>
      <StudioHeader
        kicker="Studio · Brands"
        title="Brand pages."
        sub="A row here is a page at modaboard.com/brands/<slug>. Adding one publishes it."
        right={<button onClick={startNew} style={brandBtnPrimary}>Add brand</button>}
      />

      {msg && (
        <div style={{
          marginBottom: 14, padding: '11px 14px', borderRadius: 10, fontSize: 12.5, lineHeight: 1.45,
          background: msg.kind === 'error' ? 'rgba(168,51,26,0.07)' : '#F2EFE7',
          border: '1px solid ' + (msg.kind === 'error' ? 'rgba(168,51,26,0.25)'
                                : msg.kind === 'warn' ? 'rgba(168,120,26,0.35)' : 'rgba(20,20,18,0.08)'),
          color: msg.kind === 'error' ? '#a8331a' : '#3a3a36',
        }}>{msg.text}</div>
      )}

      {(pending.length > 0 || baking) && (
        <div data-bake-banner style={{
          marginBottom: 14, padding: '13px 16px', borderRadius: 10,
          background: '#F2EFE7', border: '1px solid rgba(20,20,18,0.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap',
        }}>
          <div style={{ fontSize: 12.5, color: '#3a3a36', lineHeight: 1.5, maxWidth: '62ch' }}>
            {baking
              ? `Removing backgrounds — ${baking.done + 1} of ${baking.total} (${baking.name})…`
              : <>
                  {pending.length} brand image{pending.length === 1 ? '' : 's'} still {pending.length === 1 ? 'has' : 'have'} a
                  background stored. The pages cut it out as they render, so they already look right — doing it once
                  here stores the result, and every visit after this stops paying for it.
                </>}
          </div>
          {!baking && (
            <button onClick={bakeAll} style={brandBtnPrimary}>Remove backgrounds</button>
          )}
        </div>
      )}

      {editing && (
        <Card title={editing.id ? 'Edit brand' : 'New brand'} style={{ marginBottom: 16 }}>
          <BrandForm
            draft={editing}
            busy={busy}
            step={step}
            onChange={setEditing}
            onCancel={() => setEditing(null)}
            onSave={save}
          />
        </Card>
      )}

      <Card padding={0}>
        {loading && <div style={{ padding: 20, fontSize: 13, color: '#8a8580' }}>Loading brands…</div>}

        {!loading && brands.length === 0 && (
          <div style={{ padding: 28, textAlign: 'center' }}>
            <div style={{ fontSize: 15, color: '#3a3a36' }}>No brands yet.</div>
            <p style={{ marginTop: 8, fontSize: 13, color: '#8a8580' }}>
              Add one and it gets a page immediately.
            </p>
          </div>
        )}

        {!loading && brands.map((b, i) => (
          <div key={b.id} data-brand-row={b.slug} style={{
            display: 'grid', gridTemplateColumns: '52px 1fr auto', gap: 14, alignItems: 'center',
            padding: '14px 18px',
            borderTop: i === 0 ? 'none' : '1px solid rgba(20,20,18,0.06)',
          }}>
            <div style={{
              width: 52, height: 52, borderRadius: 10, overflow: 'hidden', background: '#FAFAF7',
              border: '1px solid rgba(20,20,18,0.08)', display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              {(b.heroImageTransparentUrl || b.heroImageUrl)
                ? <img src={b.heroImageTransparentUrl || b.heroImageUrl} alt=""
                       data-cut={b.heroImageTransparentUrl ? 'yes' : 'no'}
                       style={{
                         width: '100%', height: '100%', objectFit: 'contain', padding: 4, boxSizing: 'border-box',
                         mixBlendMode: b.heroImageTransparentUrl ? 'normal' : 'multiply',
                       }}/>
                : <span style={{ fontFamily: "'Instrument Serif',serif", fontStyle: 'italic', color: '#c9c5bd', fontSize: 18 }}>{(b.name || '?')[0]}</span>}
            </div>

            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 14.5 }}>{b.name}</div>
              <div style={{ fontSize: 11.5, color: '#8a8580', fontFamily: 'ui-monospace, monospace' }}>
                /brands/{b.slug}
              </div>
              <div style={{ fontSize: 11.5, color: '#8a8580', marginTop: 2 }}>
                {[b.category, b.estYear && 'Est. ' + b.estYear, b.location].filter(Boolean).join(' · ')}
                {' · '}{b.outfitCount} {b.outfitCount === 1 ? 'outfit' : 'outfits'}
              </div>
            </div>

            <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
              <button onClick={() => go && go({ name: 'brand', slug: b.slug })} style={brandBtnGhost}>View</button>
              <button onClick={() => startEdit(b)} style={brandBtnGhost}>Edit</button>
              {confirmId === b.id ? (
                <>
                  <button onClick={() => remove(b)} disabled={busy} style={{ ...brandBtnPrimary, background: '#a8331a' }}>
                    {busy ? 'Deleting…' : 'Delete for good'}
                  </button>
                  <button onClick={() => setConfirmId(null)} style={brandBtnGhost}>Cancel</button>
                </>
              ) : (
                <button onClick={() => setConfirmId(b.id)} style={{ ...brandBtnGhost, color: '#a8331a' }}>Delete</button>
              )}
            </div>
          </div>
        ))}
      </Card>
    </div>
  );
}

function BrandForm({ draft, busy, step, onChange, onCancel, onSave }) {
  const set = (k, v) => onChange({ ...draft, [k]: v });
  // The slug is what the URL will be, so show it being derived rather than
  // letting someone discover it afterwards.
  const slugPreview = window.MBBrands.slugify(draft.slug || draft.name) || 'your-brand';

  return (
    <div style={{ display: 'grid', gap: 14 }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <BrandField label="Name" value={draft.name} onChange={(v) => set('name', v)} placeholder="On"/>
        <BrandField label="Slug" value={draft.slug} onChange={(v) => set('slug', v)} placeholder="on"
                    hint={<>modaboard.com/brands/<b>{slugPreview}</b></>}/>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
        <BrandField label="Category" value={draft.category} onChange={(v) => set('category', v)} placeholder="Performance running"/>
        <BrandField label="Founded" value={draft.estYear} onChange={(v) => set('estYear', v.replace(/[^\d]/g, '').slice(0, 4))} placeholder="2010"/>
        <BrandField label="Location" value={draft.location} onChange={(v) => set('location', v)} placeholder="Zürich, Switzerland"/>
      </div>
      <BrandField label="Story" value={draft.story} onChange={(v) => set('story', v)} multiline
                  placeholder="What the brand is and where it came from."/>
      <BrandField label="Note on modaBoard" value={draft.platformNote} onChange={(v) => set('platformNote', v)} multiline
                  placeholder="How the brand shows up on the platform."/>
      <BrandField label="Tags" value={draft.tags} onChange={(v) => set('tags', v)} placeholder="running, performance"
                  hint="Comma separated, up to twelve."/>

      <BrandHeroField draft={draft} set={set} onChange={onChange}/>

      <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
        <button onClick={onSave} disabled={busy} style={{ ...brandBtnPrimary, opacity: busy ? 0.6 : 1 }}>
          {busy ? (step || 'Saving…') : (draft.id ? 'Save changes' : 'Create brand')}
        </button>
        <button onClick={onCancel} style={brandBtnGhost}>Cancel</button>
      </div>
    </div>
  );
}

// Upload a picture or point at one, and say whether its background should go.
// The preview is the picture as chosen — the cutout is made on save, because
// that is when there is a slug to file it under.
function BrandHeroField({ draft, set, onChange }) {
  const preview = draft.filePreview || draft.heroImageTransparentUrl || draft.heroImageUrl || '';
  const pick = (e) => {
    const file = (e.target.files || [])[0];
    if (!file) return;
    const complaint = window.MBBrands.checkHeroFile(file);
    if (complaint) { onChange({ ...draft, file: null, filePreview: null, fileError: complaint }); return; }
    // The file and its preview land in one change. Two calls to set() would
    // both build on the same stale draft, and the second would drop the first.
    const reader = new FileReader();
    reader.onload = () => onChange({
      ...draft, file, filePreview: String(reader.result || ''), fileError: null,
    });
    reader.onerror = () => onChange({ ...draft, file: null, filePreview: null, fileError: 'That file could not be read.' });
    reader.readAsDataURL(file);
  };

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '104px 1fr', gap: 14, alignItems: 'start' }}>
      <div style={{
        width: 104, height: 104, borderRadius: 12, overflow: 'hidden',
        background: '#FAFAF7', border: '1px solid rgba(20,20,18,0.10)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {preview
          ? <img src={preview} alt="" data-hero-preview
                 style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', padding: 8, boxSizing: 'border-box' }}/>
          : <span style={{ fontFamily: "'Instrument Serif',serif", fontStyle: 'italic', color: '#c9c5bd', fontSize: 22 }}>
              {(draft.name || '?')[0]}
            </span>}
      </div>

      <div style={{ display: 'grid', gap: 10 }}>
        <BrandField label="Hero image URL" value={draft.heroImageUrl} onChange={(v) => set('heroImageUrl', v)}
                    placeholder="https://…/on.jpg"
                    hint="A full https:// URL, or a path on this site like /assets/products/on.jpg. Without one the page sets the name as a wordmark."/>
        <div>
          <label style={{ display: 'block', fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580', marginBottom: 6 }}>
            …or upload one
          </label>
          <input type="file" accept="image/jpeg,image/png,image/webp" onChange={pick} data-hero-file
                 style={{ fontSize: 12, color: '#3a3a36' }}/>
          {draft.file && <div style={{ marginTop: 5, fontSize: 11, color: '#8a8580' }}>{draft.file.name} — uploaded on save.</div>}
          {draft.fileError && <div style={{ marginTop: 5, fontSize: 11, color: '#a8331a' }}>{draft.fileError}</div>}
        </div>
        <label style={{ display: 'flex', gap: 8, alignItems: 'flex-start', fontSize: 12.5, color: '#3a3a36', cursor: 'pointer' }}>
          <input type="checkbox" checked={!!draft.removeBg} data-remove-bg
                 onChange={(e) => set('removeBg', e.target.checked)} style={{ marginTop: 2 }}/>
          <span>
            Remove the background, so the image sits on the page the way product shots do.
            {draft.heroImageTransparentUrl && !draft.removeBg && <> Turning this off drops the cutout already stored.</>}
          </span>
        </label>
      </div>
    </div>
  );
}

function BrandField({ label, value, onChange, placeholder, hint, multiline }) {
  const box = {
    width: '100%', padding: '10px 12px', borderRadius: 10,
    border: '1px solid rgba(20,20,18,0.14)', background: '#fff',
    fontSize: 13, fontFamily: 'inherit', outline: 'none',
  };
  return (
    <div>
      <label style={{ display: 'block', fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580', marginBottom: 6 }}>
        {label}
      </label>
      {multiline
        ? <textarea value={value || ''} onChange={(e) => onChange(e.target.value)} placeholder={placeholder}
                    style={{ ...box, minHeight: 88, lineHeight: 1.5, resize: 'vertical' }}/>
        : <input value={value || ''} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} style={box}/>}
      {hint && <div style={{ marginTop: 5, fontSize: 11, color: '#8a8580' }}>{hint}</div>}
    </div>
  );
}

const brandBtnPrimary = {
  appearance: 'none', border: 'none', background: '#1a1a18', color: '#FAFAF7',
  fontSize: 11, padding: '9px 14px', letterSpacing: '0.12em', textTransform: 'uppercase',
  fontWeight: 500, borderRadius: 999, cursor: 'pointer', fontFamily: 'inherit',
};
const brandBtnGhost = {
  appearance: 'none', border: '1px solid rgba(20,20,18,0.16)', background: 'transparent',
  color: '#1a1a18', fontSize: 11, padding: '9px 12px', letterSpacing: '0.12em',
  textTransform: 'uppercase', fontWeight: 500, borderRadius: 999, cursor: 'pointer', fontFamily: 'inherit',
};

Object.assign(window, { StudioBrands, BrandForm, BrandField, BrandHeroField });
