// studio-views-a.jsx — Overview, Outfits, Products, Outfit Detail Analytics

const { useState: useStateVA, useMemo: useMemoVA, useEffect: useEffectVA } = React;

// Currency-aware compact price string used across the products table. Storage
// keeps a number and an ISO code; MBMoney.format is the one place that turns
// the pair back into something readable.
function fmtPriceC(v, currency) {
  return MBMoney.format(v, currency);
}

// ─── Overview ──────────────────────────────────────────────────────────────
// Everything on this screen is derived from the signed-in user's own rows in
// the Supabase `outfits` table (RLS scopes the query to them).
//
// Performance metrics — affiliate revenue, product clicks, conversion rate,
// outfit views, CTR, earnings per outfit, followers — have no data source in
// the product yet. They are deliberately absent rather than filled with demo
// numbers; see the "not tracked yet" note at the foot of the screen.

const RANGE_DAYS = { '7D': 7, '30D': 30, '90D': 90 };

function greetingFor(date) {
  const h = date.getHours();
  if (h < 12) return 'Good morning';
  if (h < 18) return 'Good afternoon';
  return 'Good evening';
}

// Bucket the user's outfits by creation date across the selected window.
// Always returns at least two points so the chart has a line to draw.
function buildOutfitSeries(outfits, range) {
  const now = new Date();
  const times = outfits.map(o => +new Date(o.created_at)).filter(t => !isNaN(t));

  // Monthly buckets for the long ranges.
  if (range === '12M' || range === 'All') {
    let months = 12;
    if (range === 'All' && times.length) {
      const oldest = Math.min(...times);
      const spanned = (now.getFullYear() - new Date(oldest).getFullYear()) * 12
                    + (now.getMonth() - new Date(oldest).getMonth()) + 1;
      months = Math.max(2, Math.min(36, spanned));
    }
    const buckets = [];
    for (let i = months - 1; i >= 0; i--) {
      const start = new Date(now.getFullYear(), now.getMonth() - i, 1);
      const end = new Date(now.getFullYear(), now.getMonth() - i + 1, 1);
      buckets.push({
        from: +start, to: +end, value: 0,
        label: start.toLocaleDateString(undefined, { month: 'short', year: '2-digit' }),
      });
    }
    times.forEach(t => { const b = buckets.find(b => t >= b.from && t < b.to); if (b) b.value++; });
    return buckets.map(b => ({ value: b.value, label: b.label }));
  }

  // Daily buckets otherwise.
  const days = RANGE_DAYS[range] || 30;
  const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const buckets = [];
  for (let i = days - 1; i >= 0; i--) {
    const start = new Date(midnight); start.setDate(start.getDate() - i);
    const end = new Date(start); end.setDate(end.getDate() + 1);
    buckets.push({
      from: +start, to: +end, value: 0,
      label: i === 0 ? 'today' : start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }),
    });
  }
  times.forEach(t => { const b = buckets.find(b => t >= b.from && t < b.to); if (b) b.value++; });
  return buckets.map(b => ({ value: b.value, label: b.label }));
}

function StudioOverview({ navTo, me, go }) {
  const session = window.useSession ? window.useSession() : null;
  const [outfits, setOutfits] = useStateVA([]);
  const [loading, setLoading] = useStateVA(true);
  const [error, setError]     = useStateVA(null);
  const [range, setRange]     = useStateVA('30D');

  const load = async () => {
    setLoading(true);
    setError(null);
    const res = await window.MBOutfits.list();
    if (res.error) { setError(res.error); setOutfits([]); }
    else setOutfits(res.outfits || []);
    setLoading(false);
  };

  useEffectVA(() => {
    if (!session) { setOutfits([]); setLoading(false); return; }
    load();
  }, [session && session.id]);

  // Real, derived figures — no estimates, no placeholders.
  const stats = useMemoVA(() => {
    const days = RANGE_DAYS[range];
    const cutoff = days ? Date.now() - days * 86400000 : null;
    const inRange = cutoff
      ? outfits.filter(o => +new Date(o.created_at) >= cutoff)
      : outfits;
    const products = outfits.reduce((n, o) => n + ((o.items || []).length), 0);
    return { total: outfits.length, inRange: inRange.length, products };
  }, [outfits, range]);

  // Real engagement for the same window, from the events table.
  const [engagement, setEngagement] = useStateVA(null);
  useEffectVA(() => {
    if (!session) { setEngagement(null); return; }
    let cancelled = false;
    (async () => {
      const days = RANGE_DAYS[range] || (range === '12M' ? 365 : null);
      const to = new Date();
      const from = days ? new Date(to.getTime() - days * 86400000) : new Date(0);
      const res = await window.MBAnalytics.totals(from, to);
      if (cancelled) return;
      setEngagement(res.error ? null : res.totals);
    })();
    return () => { cancelled = true; };
  }, [session && session.id, range]);

  const series = useMemoVA(() => buildOutfitSeries(outfits, range), [outfits, range]);
  const recent = useMemoVA(
    () => [...outfits]
      .sort((a, b) => +new Date(b.created_at) - +new Date(a.created_at))
      .slice(0, 5),
    [outfits]
  );

  const rangeLabel = range === 'All' ? 'all time'
    : range === '12M' ? 'the last 12 months'
    : 'the last ' + (RANGE_DAYS[range] || 30) + ' days';
  const firstName = (me.name || '').split(' ')[0];

  const openOutfit = (o) => {
    if (go) go({ name: 'dashboard', savedId: o.id });
  };

  return (
    <div>
      <StudioHeader
        kicker="Studio · Dashboard"
        title={<span>{greetingFor(new Date())}, <em style={{ fontFamily: "'Instrument Serif',serif", fontStyle: 'italic' }}>{firstName}</em>.</span>}
        sub="Your saved outfits, straight from your account."
        right={<RangeSelect value={range} onChange={setRange}/>}
      />

      {error && (
        <Card style={{ marginBottom: 14 }}>
          <div style={{ fontSize: 13, color: '#a8331a', lineHeight: 1.5 }}>{error}</div>
          <button onClick={load} style={{ ...linkBtn, marginTop: 10 }}>Try again →</button>
        </Card>
      )}

      {loading && (
        <Card style={{ marginBottom: 14 }}>
          <div style={{ fontSize: 13, color: '#8a8580' }}>Loading your outfits…</div>
        </Card>
      )}

      {!loading && !error && (
        <>
          {/* Real stats, derived from the outfits table */}
          <div className="dc-stat-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 14 }}>
            <StatCard label="Saved outfits" value={stats.total} sublabel="all time"/>
            <StatCard label="Created" value={stats.inRange} sublabel={'in ' + rangeLabel}/>
            <StatCard label="Products placed" value={stats.products} sublabel="across all outfits"/>
          </div>

          {/* Real engagement on published outfits */}
          <div className="dc-stat-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 14 }}>
            <StatCard label="Outfit views"    value={engagement ? engagement.views : 0}          sublabel={'in ' + rangeLabel}/>
            <StatCard label="Product clicks"  value={engagement ? engagement.productClicks : 0}  sublabel="taps on a piece"/>
            <StatCard label="Clicks to store" value={engagement ? engagement.outboundClicks : 0} sublabel="left for the retailer"/>
          </div>

          {/* Outfits over time, from created_at */}
          <Card title="Outfits over time"
                sub={'When you saved outfits · ' + rangeLabel}
                style={{ marginBottom: 14 }}>
            {stats.total === 0 ? (
              <div style={{ padding: '32px 0', textAlign: 'center', fontSize: 13, color: '#8a8580' }}>
                Nothing to chart yet — save your first outfit to start the timeline.
              </div>
            ) : (
              <div style={{ marginTop: 8 }}>
                <AreaChart
                  data={series}
                  height={220}
                  valueFmt={(v) => v + (v === 1 ? ' outfit' : ' outfits')}
                  labelFmt={(d) => d.label}
                />
              </div>
            )}
          </Card>

          {/* Most recent outfits */}
          <Card title="Recent outfits"
                sub={stats.total ? stats.total + ' saved in total' : undefined}
                right={<button onClick={() => go && go({ name: 'myoutfits' })} style={linkBtn}>View all →</button>}
                style={{ marginBottom: 14 }}>
            {recent.length === 0 ? (
              <div style={{ padding: '28px 0', textAlign: 'center' }}>
                <div style={{ fontSize: 14, marginBottom: 6 }}>No saved outfits yet.</div>
                <div style={{ fontSize: 12.5, color: '#8a8580', marginBottom: 16 }}>
                  Build a look in the outfit editor and hit Save outfit.
                </div>
                <button onClick={() => go && go({ name: 'dashboard' })} style={{
                  appearance: 'none', border: 'none', background: '#1a1a18', color: '#FAFAF7',
                  fontSize: 11, padding: '10px 18px', letterSpacing: '0.14em',
                  textTransform: 'uppercase', fontWeight: 500, borderRadius: 999,
                  cursor: 'pointer', fontFamily: 'inherit',
                }}>Create an outfit</button>
              </div>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                {recent.map((o, i) => (
                  <div key={o.id} onClick={() => openOutfit(o)}
                       style={{
                         display: 'grid', gridTemplateColumns: '24px 72px 1fr 110px 130px', gap: 16, alignItems: 'center',
                         padding: '10px 8px', borderRadius: 10, cursor: 'pointer',
                         borderBottom: i < recent.length - 1 ? '1px solid rgba(20,20,18,0.04)' : 'none',
                       }}>
                    <div style={{ fontSize: 18, color: '#8a8580', fontFamily: "'Instrument Serif',serif", fontStyle: 'italic' }}>0{i + 1}</div>
                    <div style={{ width: 72, height: 72, borderRadius: 10, overflow: 'hidden', background: '#FAFAF7', border: '1px solid rgba(20,20,18,0.06)' }}>
                      <OutfitBoard outfit={{ items: o.items || [] }}/>
                    </div>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: 14, letterSpacing: '-0.005em', fontFamily: "'Instrument Serif',serif", fontStyle: 'italic' }}>
                        {o.title || 'Untitled outfit'}
                      </div>
                      {o.mood && (
                        <div style={{ fontSize: 11.5, color: '#8a8580', marginTop: 3, letterSpacing: '0.04em' }}>{o.mood}</div>
                      )}
                    </div>
                    <Cell label="Pieces" value={(o.items || []).length}/>
                    <Cell label="Saved" value={new Date(o.created_at).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}/>
                  </div>
                ))}
              </div>
            )}
          </Card>

          {/* Honest placeholder for the metrics that have no source yet */}
          <NotTracked
            title="Still not tracked"
            body={<>Views and clicks above are real. Revenue is not: no affiliate
              network is connected, so a click through to the retailer is the
              last thing modaBoard can see — what happens after it, we don’t
              know. See Analytics for the full breakdown.</>}
            metrics={['Affiliate revenue', 'Conversion rate', 'Earnings per outfit', 'Followers']}
          />
        </>
      )}
    </div>
  );
}

function Cell({ label, value, small, accent }) {
  return (
    <div>
      <div style={{ fontSize: small ? 9.5 : 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580' }}>{label}</div>
      <div style={{ marginTop: 3, fontSize: small ? 14 : 16, fontVariantNumeric: 'tabular-nums', fontWeight: 500, letterSpacing: '-0.01em', color: accent ? '#1a6b3a' : '#1a1a18' }}>{value}</div>
    </div>
  );
}

// ─── My Outfits ────────────────────────────────────────────────────────────
function StudioOutfits({ navTo, go }) {
  const { outfits, loading, error, reload, session } = useMyOutfits();
  const [sort, setSort] = useStateVA('newest');

  const sorted = useMemoVA(() => {
    const list = [...outfits];
    if (sort === 'newest') return list.sort((a, b) => +new Date(b.created_at) - +new Date(a.created_at));
    if (sort === 'oldest') return list.sort((a, b) => +new Date(a.created_at) - +new Date(b.created_at));
    if (sort === 'pieces') return list.sort((a, b) => (b.items || []).length - (a.items || []).length);
    if (sort === 'title')  return list.sort((a, b) => (a.title || '').localeCompare(b.title || ''));
    return list;
  }, [outfits, sort]);

  return (
    <div>
      <StudioHeader
        kicker="Studio · My outfits"
        title="Your saved outfits."
        sub="Everything you've saved to your account. Click a row to open it."
        right={
          <select value={sort} onChange={(e) => setSort(e.target.value)} style={selectStyle}>
            <option value="newest">Newest first</option>
            <option value="oldest">Oldest first</option>
            <option value="pieces">Most pieces</option>
            <option value="title">Title A–Z</option>
          </select>
        }
      />

      {error && <StudioError error={error} onRetry={reload}/>}
      {loading && !error && <StudioLoading/>}

      {!loading && !error && sorted.length === 0 && (
        <StudioEmpty
          title="No saved outfits yet."
          body="Build a look in the outfit editor and hit Save outfit."
          go={go}
        />
      )}

      {!loading && !error && sorted.length > 0 && (
        <Card padding={0} className="dc-scroll-x">
          <div style={{ display: 'grid', gridTemplateColumns: '92px 1.4fr 90px 120px 1fr 60px', alignItems: 'center', padding: '14px 20px', borderBottom: '1px solid rgba(20,20,18,0.06)', fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580' }}>
            <div>Outfit</div>
            <div>Title</div>
            <div>Pieces</div>
            <div>Saved</div>
            <div>Public link</div>
            <div></div>
          </div>
          {sorted.map((o, i) => (
            <div key={o.id} onClick={() => navTo({ name: 'outfit-detail', id: o.id })} style={{
              display: 'grid', gridTemplateColumns: '92px 1.4fr 90px 120px 1fr 60px',
              alignItems: 'center', padding: '14px 20px',
              borderBottom: i < sorted.length - 1 ? '1px solid rgba(20,20,18,0.04)' : 'none',
              cursor: 'pointer', transition: 'background .15s',
            }} onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(20,20,18,0.02)'}
               onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
              <div style={{ width: 72, height: 72, borderRadius: 10, overflow: 'hidden', background: '#FAFAF7', border: '1px solid rgba(20,20,18,0.05)' }}>
                <OutfitBoard outfit={{ items: o.items || [] }}/>
              </div>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 14, letterSpacing: '-0.005em', fontFamily: "'Instrument Serif',serif", fontStyle: 'italic', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{o.title || 'Untitled outfit'}</div>
                {o.mood && <div style={{ fontSize: 11, color: '#8a8580', marginTop: 3, letterSpacing: '0.04em' }}>{o.mood}</div>}
              </div>
              <div style={{ fontSize: 14, fontVariantNumeric: 'tabular-nums' }}>{(o.items || []).length}</div>
              <div style={{ fontSize: 13, color: '#5a5a54' }}>{fmtDay(+new Date(o.created_at))}</div>
              <div style={{ minWidth: 0 }}>
                <PublicUrl published={o.published} slug={o.slug} compact/>
              </div>
              <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                <span style={{ color: '#8a8580', fontSize: 16 }}>→</span>
              </div>
            </div>
          ))}
        </Card>
      )}

      {!loading && !error && sorted.length > 0 && (
        <div style={{ marginTop: 14 }}>
          <NotTracked
            title="Outfit performance"
            body="Views, clicks and revenue per outfit aren't tracked, so outfits can't be ranked by performance — only by what your account actually knows about them."
            metrics={['Views', 'Clicks', 'Click-through rate', 'Conversion', 'Revenue']}
          />
        </div>
      )}
    </div>
  );
}

// ─── Products ──────────────────────────────────────────────────────────────
// Real: every product here was placed on one of the user's own boards, so the
// product list, the brands and the usage counts are all derived from their
// saved outfits. Affiliate performance (clicks, revenue, conversion,
// commission) has no source and is not shown.
function StudioProducts({ go }) {
  const { outfits, loading, error, reload } = useMyOutfits();
  const [sort, setSort] = useStateVA('used');
  const [overrides, setOverrides] = useStateVA({});   // keyed by brand|name
  const [editing, setEditing] = useStateVA(null);
  const [rechecking, setRechecking] = useStateVA(null);
  const [toast, setToast] = useStateVA(null);

  const withOverrides = (p) => ({ ...p, ...(overrides[p.key] || {}) });

  // A product row is one piece as it appears across every board it's on, so
  // saving an edit writes to each of those outfits. Only the items change —
  // title, mood and notes are left alone.
  const persistProductEdit = async (key, next) => {
    const matches = (it) =>
      ((it.brand || '').trim().toLowerCase() + '|' + (it.name || '').trim().toLowerCase()) === key;
    const money = MBMoney.split(next.price, next.productUrl, next.currency);
    const touched = (outfits || []).filter(o => (o.items || []).some(matches));
    if (!touched.length) return { error: 'That product isn’t on any of your saved outfits any more.' };

    const results = await Promise.all(touched.map(o => window.MBOutfits.setItems(o.id, o.items.map(it => (
      matches(it) ? Object.assign({}, it, {
        brand: next.brand,
        name: next.name,
        store: next.store,
        sourceUrl: next.productUrl || it.sourceUrl,
        price: money.price,
        currency: money.currency,
      }) : it
    )))));
    const failed = results.find(r => r && r.error);
    return failed ? { error: failed.error } : { count: touched.length };
  };

  // Re-scrape the product page for its live price via the real /api endpoint.
  // If that endpoint isn't reachable we say so — the old code invented a
  // random markdown here, which reported price drops that never happened.
  const recheckPrice = async (p) => {
    if (rechecking) return;
    setRechecking(p.key);
    try {
      const url = p.productUrl || p.sourceUrl;
      if (!url) {
        setToast('No product URL saved for this item — add one to check its price.');
        return;
      }
      if (!window.openaiProductFallback) {
        setToast('Price checking needs the product API, which isn’t available here.');
        return;
      }
      const r = await window.openaiProductFallback(url);
      // The endpoint returns price as a number already.
      const live = r && r.price != null ? Number(r.price) : null;
      if (live == null || isNaN(live)) {
        setToast('Couldn’t read a current price from that page.');
        return;
      }
      const current = MBMoney.parseAmount(p.price) || 0;
      const cur = r.currency || p.currency;
      if (current && live < current) {
        setOverrides(prev => ({ ...prev, [p.key]: { ...(prev[p.key] || {}), price: live, wasPrice: current, onSale: true, currency: cur } }));
        setToast(`Price drop — ${p.name} now ${fmtPriceC(live, cur)} (was ${fmtPriceC(current, p.currency)})`);
      } else {
        setToast(`${p.name} is listed at ${fmtPriceC(live, cur)}.`);
      }
    } catch {
      setToast('Couldn’t recheck the price right now.');
    } finally {
      setRechecking(null);
      setTimeout(() => setToast(null), 3600);
    }
  };

  const products = useMemoVA(() => deriveProducts(outfits), [outfits]);
  const list = useMemoVA(() => {
    const r = products.map(withOverrides);
    return [...r].sort((a, b) => {
      if (sort === 'used')   return b.outfitCount - a.outfitCount || a.name.localeCompare(b.name);
      if (sort === 'recent') return b.lastUsed - a.lastUsed;
      if (sort === 'brand')  return a.brand.localeCompare(b.brand) || a.name.localeCompare(b.name);
      if (sort === 'name')   return a.name.localeCompare(b.name);
      return 0;
    });
  }, [products, sort, overrides]);

  const topBrands = useMemoVA(() => deriveBrands(products).slice(0, 5), [products]);

  return (
    <div>
      <StudioHeader
        kicker="Studio · Products"
        title="Products in your outfits."
        sub="Every product you've placed on a board, with how often you've used it."
        right={
          <select value={sort} onChange={(e) => setSort(e.target.value)} style={selectStyle}>
            <option value="used">Most used</option>
            <option value="recent">Recently used</option>
            <option value="brand">Brand A–Z</option>
            <option value="name">Name A–Z</option>
          </select>
        }
      />

      {error && <StudioError error={error} onRetry={reload}/>}
      {loading && !error && <StudioLoading label="Loading your products…"/>}

      {!loading && !error && list.length === 0 && (
        <StudioEmpty
          title="No products yet."
          body="Products appear here once you add them to an outfit and save it."
          go={go}
        />
      )}

      {!loading && !error && list.length > 0 && (
        <>
          {/* Brands you actually use, by how many placements they have */}
          <div className="dc-stat-grid" style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(5, Math.max(1, topBrands.length))}, 1fr)`, gap: 10, marginBottom: 14 }}>
            {topBrands.map((b, i) => (
              <div key={b.brand} style={{
                padding: 14, borderRadius: 12,
                background: i === 0 ? '#1a1a18' : '#FAFAF7',
                color: i === 0 ? '#FAFAF7' : '#1a1a18',
                border: '1px solid ' + (i === 0 ? '#1a1a18' : 'rgba(20,20,18,0.06)'),
              }}>
                <div style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', color: i === 0 ? 'rgba(250,249,247,0.6)' : '#8a8580' }}>#{i + 1} brand</div>
                <div style={{ marginTop: 8, fontSize: 17, letterSpacing: '-0.01em', fontWeight: 500 }}>{b.brand}</div>
                <div style={{ marginTop: 6, fontSize: 12, fontVariantNumeric: 'tabular-nums', opacity: 0.7 }}>
                  {b.products} {b.products === 1 ? 'product' : 'products'}
                </div>
                <div style={{ marginTop: 2, fontSize: 14, fontVariantNumeric: 'tabular-nums', fontWeight: 500 }}>
                  {b.placements} {b.placements === 1 ? 'placement' : 'placements'}
                </div>
              </div>
            ))}
          </div>

          <Card padding={0} className="dc-scroll-x">
            <div style={{ display: 'grid', gridTemplateColumns: '64px 1.6fr 110px 140px 150px 80px', alignItems: 'center', padding: '14px 20px', borderBottom: '1px solid rgba(20,20,18,0.06)', fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580' }}>
              <div></div><div>Product</div><div>Outfits</div><div>First used</div><div>Last used</div><div></div>
            </div>
            {list.map((p, i) => (
              <div key={p.key} style={{
                display: 'grid', gridTemplateColumns: '64px 1.6fr 110px 140px 150px 80px',
                alignItems: 'center', padding: '12px 20px',
                borderBottom: i < list.length - 1 ? '1px solid rgba(20,20,18,0.04)' : 'none',
              }}>
                <div style={{ width: 48, height: 48, borderRadius: 8, background: '#F4F2EC', border: '1px solid rgba(20,20,18,0.05)' }}>
                  <ProductImage item={p}/>
                </div>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 10.5, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#8a8580' }}>{p.brand}</div>
                  <div style={{ fontSize: 13.5, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.name}</div>
                  <div style={{ fontSize: 11, color: '#8a8580', marginTop: 2 }}>
                    {p.onSale && p.wasPrice && <span style={{ textDecoration: 'line-through', marginRight: 5, opacity: 0.7 }}>{fmtPriceC(p.wasPrice, p.currency)}</span>}
                    {p.price ? <span style={p.onSale ? { color: '#1a6b3a', fontWeight: 500 } : undefined}>{fmtPriceC(p.price, p.currency)}</span> : <span>No price saved</span>}
                    {p.onSale && <span style={{ marginLeft: 5, color: '#1a6b3a' }}>· sale</span>}
                    {p.store ? ' · ' + p.store : ''}
                  </div>
                </div>
                <div style={{ fontSize: 13.5, fontVariantNumeric: 'tabular-nums' }}>{p.outfitCount}</div>
                <div style={{ fontSize: 13, color: '#5a5a54' }}>{fmtDay(p.firstUsed)}</div>
                <div style={{ fontSize: 13, color: '#5a5a54' }}>{fmtDay(p.lastUsed)}</div>
                <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}>
                  <button title="Edit product" onClick={() => setEditing({ key: p.key, draft: (() => {
                    const money = MBMoney.split(p.price, p.productUrl || p.sourceUrl, p.currency);
                    return {
                      brand: p.brand, name: p.name, store: p.store,
                      price: money.price != null ? String(money.price) : '',
                      currency: money.currency || '',
                      productUrl: p.productUrl || p.sourceUrl || '',
                    };
                  })() })} style={miniBtn}><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#1a1a18" strokeWidth="1.8"><path d="M9 17H5v-4L17 1l4 4z"/></svg></button>
                  <button title="Recheck price" onClick={() => recheckPrice(p)} disabled={rechecking === p.key} style={{ ...miniBtn, cursor: rechecking === p.key ? 'progress' : 'pointer' }}>
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#1a1a18" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={rechecking === p.key ? { animation: 'dc-spin .8s linear infinite' } : undefined}><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v5h-5"/></svg>
                  </button>
                </div>
              </div>
            ))}
          </Card>

          <div style={{ marginTop: 14 }}>
            <NotTracked
              title="Affiliate performance"
              body="No affiliate network is connected and no click or order data is collected, so products can't be ranked by earnings. Product details you edit here are saved to your outfits; the metrics below are what has no source."
              metrics={['Clicks', 'Revenue', 'Conversion', 'Commission', 'Trend']}
            />
          </div>
        </>
      )}

      {editing && (
        <StudioProductEditModal
          draft={editing.draft}
          onClose={() => setEditing(null)}
          onSave={async (next) => {
            const key = editing.key;
            setEditing(null);
            setToast('Saving…');
            const res = await persistProductEdit(key, next);
            if (res.error) {
              setToast(res.error);
            } else {
              // Clear any session-only override for this row: the database is
              // the answer now.
              setOverrides(prev => { const p = { ...prev }; delete p[key]; return p; });
              await reload();
              setToast(res.count === 1 ? 'Product saved' : `Product saved across ${res.count} outfits`);
            }
            setTimeout(() => setToast(null), 3000);
          }}
        />
      )}
      {toast && (
        <div style={{
          position: 'fixed', bottom: 24, left: '50%', transform: 'translateX(-50%)',
          padding: '12px 18px', borderRadius: 999, background: '#1a1a18', color: '#FAFAF7',
          fontSize: 12.5, zIndex: 1200, boxShadow: '0 16px 40px rgba(20,20,18,0.18)',
          display: 'flex', alignItems: 'center', gap: 10,
        }}><span style={{ fontFamily: "'Instrument Serif',serif", fontStyle: 'italic', opacity: 0.6 }}>✓</span>{toast}</div>
      )}
    </div>
  );
}

function StudioProductEditModal({ draft, onClose, onSave }) {
  const [d, setD] = useStateVA(draft);
  const set = (k, v) => setD(prev => ({ ...prev, [k]: v }));
  const lbl = { display: 'block', fontSize: 10.5, letterSpacing: '0.14em', textTransform: 'uppercase', color: '#8a8580', marginBottom: 6 };
  const inp = { 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 onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 1150, background: 'rgba(20,20,18,0.42)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, backdropFilter: 'blur(2px)',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxWidth: 460, background: '#FAFAF7', borderRadius: 18,
        border: '1px solid rgba(20,20,18,0.08)', padding: 28,
        boxShadow: '0 30px 80px -20px rgba(20,20,18,0.35)', maxHeight: '90vh', overflowY: 'auto',
      }}>
        <div style={{ fontSize: 11, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#8a8580' }}>Edit product</div>
        <h3 style={{ margin: '8px 0 18px', fontSize: 24, fontWeight: 400, letterSpacing: '-0.02em' }}>{d.name}</h3>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div><label style={lbl}>Brand</label><input style={inp} value={d.brand} onChange={(e) => set('brand', e.target.value)}/></div>
          <div><label style={lbl}>Product name</label><input style={inp} value={d.name} onChange={(e) => set('name', e.target.value)}/></div>
          <div><label style={lbl}>E-commerce / store</label><input style={inp} value={d.store} onChange={(e) => set('store', e.target.value)} placeholder="e.g. SSENSE, Sneakersnstuff"/></div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 110px', gap: 12 }}>
            <div><label style={lbl}>Price</label><input style={inp} value={d.price} onChange={(e) => set('price', e.target.value.replace(/[^\d.]/g, ''))} inputMode="decimal"/></div>
            <div><label style={lbl}>Currency</label>
              <select style={{ ...inp, cursor: 'pointer' }} value={d.currency} onChange={(e) => set('currency', e.target.value)}>
                <option value="">Unknown</option>
                {MBMoney.CURRENCY_OPTIONS.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
          </div>
          <div><label style={lbl}>Product URL</label><input style={inp} value={d.productUrl} onChange={(e) => set('productUrl', e.target.value)} placeholder="https://store.com/product"/></div>
        </div>
        <div style={{ marginTop: 22, display: 'flex', gap: 10 }}>
          <button onClick={() => onSave(d)} style={{ ...btnPrimary, flex: 1 }}>Save changes</button>
          <button onClick={onClose} style={btnGhost}>Cancel</button>
        </div>
      </div>
    </div>
  );
}

const miniBtn = {
  appearance: 'none', border: '1px solid rgba(20,20,18,0.08)', background: '#FAFAF7',
  width: 26, height: 26, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
};

// ─── Outfit Detail Analytics ───────────────────────────────────────────────
// Real outfit, loaded by id from the user's own rows. Engagement metrics —
// the click heatmap, traffic sources and per-outfit revenue that used to fill
// this screen — have no source and are replaced by one honest panel.
function StudioOutfitDetail({ outfitId, navTo, go }) {
  const [outfit, setOutfit] = useStateVA(null);
  const [loading, setLoading] = useStateVA(true);
  const [error, setError] = useStateVA(null);

  const load = async () => {
    setLoading(true);
    setError(null);
    const res = await window.MBOutfits.get(outfitId);
    if (res.error) { setError(res.error); setOutfit(null); }
    else setOutfit(res.outfit);
    setLoading(false);
  };

  useEffectVA(() => { if (outfitId) load(); else setLoading(false); }, [outfitId]);

  const items = (outfit && outfit.items) || [];

  return (
    <div>
      <div style={{ marginBottom: 16 }}>
        <button onClick={() => navTo({ name: 'outfits' })} style={{ ...linkBtn, color: '#5a5a54' }}>← All outfits</button>
      </div>

      {error && <StudioError error={error} onRetry={load}/>}
      {loading && !error && <StudioLoading label="Loading outfit…"/>}

      {!loading && !error && !outfit && (
        <StudioEmpty title="Outfit not found." body="It may have been deleted." go={go}/>
      )}

      {!loading && !error && outfit && (
        <>
          <StudioHeader
            kicker={outfit.mood ? 'Outfit · ' + outfit.mood : 'Outfit'}
            title={outfit.title || 'Untitled outfit'}
            sub={`${items.length} ${items.length === 1 ? 'piece' : 'pieces'} · saved ${fmtDay(+new Date(outfit.created_at))}`}
            right={<button onClick={() => go && go({ name: 'dashboard', savedId: outfit.id })} style={btnPrimary}>Edit in builder</button>}
          />

          <Card title="Public link"
                sub={outfit.published ? 'Anyone with this link can see the outfit' : 'Not published'}
                style={{ marginBottom: 14 }}>
            <PublicUrl published={outfit.published} slug={outfit.slug}/>
          </Card>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: 14, marginBottom: 14 }}>
            <Card title="Board">
              <div style={{ borderRadius: 10, overflow: 'hidden', background: '#FAFAF7', border: '1px solid rgba(20,20,18,0.06)' }}>
                <OutfitBoard outfit={{ items }}/>
              </div>
            </Card>

            <Card title="Pieces" sub={items.length + ' in this outfit'}>
              {items.length === 0 ? (
                <div style={{ padding: '24px 0', textAlign: 'center', fontSize: 13, color: '#8a8580' }}>
                  This board has no products on it.
                </div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {items.map((it, i) => (
                    <div key={it.id || i} style={{
                      display: 'grid', gridTemplateColumns: '48px 1fr auto', gap: 14, alignItems: 'center',
                      paddingBottom: 10,
                      borderBottom: i < items.length - 1 ? '1px solid rgba(20,20,18,0.04)' : 'none',
                    }}>
                      <div style={{ width: 48, height: 48, borderRadius: 8, background: '#F4F2EC', border: '1px solid rgba(20,20,18,0.05)' }}>
                        <ProductImage item={it}/>
                      </div>
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontSize: 10.5, letterSpacing: '0.1em', textTransform: 'uppercase', color: '#8a8580' }}>{it.brand || '—'}</div>
                        <div style={{ fontSize: 13.5, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{it.name || 'Untitled product'}</div>
                        {it.store && <div style={{ fontSize: 11, color: '#8a8580', marginTop: 2 }}>{it.store}</div>}
                      </div>
                      <div style={{ fontSize: 13.5, fontVariantNumeric: 'tabular-nums', color: '#5a5a54' }}>
                        {it.price ? fmtPriceC(it.price, it.currency) : '—'}
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </Card>
          </div>

          <NotTracked
            title="Outfit performance"
            body="Nothing measures what happens to an outfit after you save it, so there are no views, no clicks and no per-item click heatmap for this board."
            metrics={['Views', 'Clicks per item', 'Click heatmap', 'Traffic sources', 'Revenue']}
          />
        </>
      )}
    </div>
  );
}

Object.assign(window, { StudioOverview, StudioOutfits, StudioProducts, StudioOutfitDetail, StudioProductEditModal, Cell, miniBtn });
