// Brand Audit — Google Maps reviews analytics (extracted from Super Admin).
// Accessible to all logged-in users via sidebar "Brand Audit" (tab Audit | Dashboard | Komentar Toko).
// SerpAPI Maps + Google Maps actor settings tetap di Super Admin → Integrasi.

function SuperAdminBrandAudit() {
  const useState_ba = React.useState;
  const useEffect_ba = React.useEffect;
  const [brand, setBrand] = useState_ba('');
  const [region, setRegion] = useState_ba('indonesia');
  const [regions, setRegions] = useState_ba([]);
  const [data, setData] = useState_ba(null);
  const [running, setRunning] = useState_ba(false);
  const [loadingCache, setLoadingCache] = useState_ba(false);
  const [history, setHistory] = useState_ba([]);
  const [generatedAt, setGeneratedAt] = useState_ba(null);
  const [searchesUsed, setSearchesUsed] = useState_ba(0);
  const [error, setError] = useState_ba(null);

  const reloadHistory = () => {
    window.api.listBrandAudits()
      .then((r) => setHistory(r.audits || []))
      .catch(() => {});
  };
  useEffect_ba(() => {
    reloadHistory();
    window.api.listAuditRegions()
      .then((r) => setRegions(r.regions || []))
      .catch(() => {});
  }, []);

  const regionLabel = (key) => regions.find((r) => r.key === key)?.name || key;

  const loadCached = async (b, regionKey) => {
    if (!b) return;
    setLoadingCache(true); setError(null);
    try {
      const r = await window.api.getBrandAudit(b, regionKey || 'indonesia');
      if (r.cached && r.data) {
        setData(r.data);
        setGeneratedAt(r.ageMs);
        setSearchesUsed(r.searchesUsed || 0);
      } else {
        setData(null);
        setGeneratedAt(null);
      }
    } catch (e) { setError(e.message); }
    finally { setLoadingCache(false); }
  };

  const runAudit = async () => {
    if (!brand.trim()) return;
    const regionName = regionLabel(region);
    if (!confirm(`Jalankan audit "${brand}" di ${regionName}?\n\nDual-fetch review (ratingLow + newestFirst) dari 10 outlet rating terendah.\nMaksimal coverage 1-star comments untuk evaluasi.\n\n~13-23 SerpAPI searches dari kuota bulanan.\nHasil di-cache 7 hari.`)) return;
    setRunning(true); setError(null);
    try {
      const r = await window.api.runBrandAudit({ brand: brand.trim(), region });
      setData(r.data);
      setGeneratedAt(0);
      setSearchesUsed(r.searchesUsed || 0);
      reloadHistory();
    } catch (e) {
      setError(e.message || 'Gagal menjalankan audit');
    } finally {
      setRunning(false);
    }
  };

  const summary = data?.summary;
  const places = data?.places || [];
  const reviewsByPlace = data?.reviewsByPlace || {};

  return (
    <>
      <div className="card" style={{ padding: 22, marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <span style={{ fontSize: 16 }}>🏪</span>
          <h2 style={{ margin: 0, fontSize: 17, fontWeight: 600, letterSpacing: '-0.01em' }}>
            Brand Audit — Outlet Rating Bedah
          </h2>
          <span style={{ fontSize: 9.5, padding: '2px 7px', borderRadius: 4, background: 'oklch(0.96 0.05 75)', color: 'oklch(0.55 0.16 75)', fontWeight: 700, marginLeft: 4 }}>TRIAL</span>
        </div>
        <p style={{ margin: '0 0 16px', fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55 }}>
          Scan <b>SEMUA outlet</b> brand di Google Maps (cap 60) → dual-fetch review (sort_by=ratingLow + newestFirst) dari 10 outlet rating terendah → AI bedah keluhan + tampilkan komentar bintang 1 mentah lengkap →
          rekomendasi action. Ideal untuk owner brand multi-outlet yang mau evaluasi pain-point dari komentar customer.
          <br/><b>Cost</b>: ~13-23 SerpAPI searches per audit (1-3 paginate + 10 outlet × 2 dual-fetch). Cache 7 hari.
        </p>

        <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr auto', gap: 10, alignItems: 'flex-end' }}>
          <div>
            <label className="label">Nama Brand</label>
            <input
              className="input"
              value={brand}
              onChange={(e) => setBrand(e.target.value)}
              placeholder="contoh: Geprekin Aja, Kopi Kenanga, Holland Bakery"
              onKeyDown={(e) => e.key === 'Enter' && runAudit()}
            />
          </div>
          <div>
            <label className="label">Wilayah</label>
            <select
              className="select"
              value={region}
              onChange={(e) => setRegion(e.target.value)}
            >
              {regions.length === 0 ? (
                <option value="indonesia">Indonesia (nasional)</option>
              ) : regions.map((r) => (
                <option key={r.key} value={r.key}>{r.name}</option>
              ))}
            </select>
          </div>
          <div style={{ display: 'flex', gap: 6 }}>
            <button
              className="btn btn-ghost"
              onClick={() => loadCached(brand, region)}
              disabled={!brand.trim() || loadingCache || running}
              style={{ padding: '10px 14px' }}
            >
              {loadingCache ? 'Cek…' : '↻ Cache'}
            </button>
            <button
              className="btn btn-primary"
              onClick={runAudit}
              disabled={!brand.trim() || running}
              style={{ padding: '10px 18px' }}
            >
              {running ? 'Scanning…' : '🔍 Audit'}
            </button>
          </div>
        </div>
      </div>

      {error && (
        <div style={{ padding: 12, borderRadius: 10, background: 'var(--danger-soft)', color: 'var(--danger)', fontSize: 13, marginBottom: 12 }}>
          {error}
        </div>
      )}

      {history.length > 0 && !data && (
        <div className="card" style={{ padding: 14, marginBottom: 14 }}>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>
            Audit terakhir
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {history.slice(0, 8).map((h, i) => (
              <button
                key={i}
                onClick={() => { setBrand(h.brandKey); setRegion(h.locationKey); loadCached(h.brandKey, h.locationKey); }}
                style={{
                  display: 'grid', gridTemplateColumns: '2fr 1fr auto auto', gap: 10,
                  alignItems: 'center', padding: '8px 10px', background: 'var(--bg)',
                  border: '1px solid var(--line)', borderRadius: 8, cursor: 'pointer',
                  textAlign: 'left', fontSize: 12.5,
                }}
              >
                <span style={{ fontWeight: 600, color: 'var(--ink)' }}>{h.brandKey}</span>
                <span style={{ color: 'var(--ink-3)' }}>{regionLabel(h.locationKey)}</span>
                <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{h.searchesUsed} search</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{new Date(h.generatedAt).toLocaleDateString('id-ID')}</span>
              </button>
            ))}
          </div>
        </div>
      )}

      {data && <BrandAuditResults data={data} summary={summary} places={places} reviewsByPlace={reviewsByPlace} searchesUsed={searchesUsed} />}
    </>
  );
}

function BrandAuditResults({ data, summary, places, reviewsByPlace, searchesUsed }) {
  const [activePlaceId, setActivePlaceId] = React.useState(null);
  const ranked = [...places].filter((p) => p.rating != null).sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0));
  const activeReviews = activePlaceId ? (reviewsByPlace[activePlaceId] || []) : [];

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {/* Header summary */}
      <div className="card" style={{ padding: 18 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
          <div>
            <div style={{ fontSize: 13, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 700 }}>{data.brand}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>
              {data.region || data.location || '—'} · {places.length} outlet di-scan · {Object.keys(reviewsByPlace).filter((k) => (reviewsByPlace[k] || []).length > 0).length} dengan review sample · {searchesUsed} SerpAPI searches
            </div>
          </div>
          {summary && (
            <div style={{ display: 'flex', gap: 8 }}>
              <SummaryStat label="Rating gabungan" value={summary.overallScore?.toFixed(1) || '—'} unit={'⭐'} />
              <SummaryStat label="Total review" value={summary.totalReviews?.toLocaleString('id-ID') || '0'} />
            </div>
          )}
        </div>
        <div style={{ marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--line)', display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
          <span style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>📄 Generate presentasi PDF dengan AI narrative + infografik:</span>
          <a
            href={`/api/admin/brand-audit/${encodeURIComponent(data.brand?.toLowerCase() ?? '')}/${encodeURIComponent(data.regionKey ?? '')}/report`}
            target="_blank"
            rel="noreferrer"
            className="btn btn-primary"
            style={{ padding: '6px 14px', fontSize: 12, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6 }}
          >
            🖨️ Print PDF Presentasi
          </a>
          <span style={{ fontSize: 10.5, color: 'var(--ink-4)', marginLeft: 'auto' }}>
            Buka di tab baru → klik "Save as PDF" di toolbar atas
          </span>
        </div>
      </div>

      {/* Top / Worst stores */}
      {summary && (summary.topStores?.length > 0 || summary.worstStores?.length > 0) && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 14 }}>
          {summary.topStores?.length > 0 && (
            <div className="card" style={{ padding: 16 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: 'oklch(0.42 0.14 155)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
                🏆 Outlet Terbaik
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {summary.topStores.map((s, i) => <StoreRow key={i} store={s} positive />)}
              </div>
            </div>
          )}
          {summary.worstStores?.length > 0 && (
            <div className="card" style={{ padding: 16 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: 'oklch(0.55 0.16 25)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
                ⚠️ Perlu Perbaikan
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {summary.worstStores.map((s, i) => <StoreRow key={i} store={s} positive={false} />)}
              </div>
            </div>
          )}
        </div>
      )}

      {/* Raw 1-star complaints — komplain mentah untuk dibaca langsung */}
      <OneStarReviewsSection places={places} reviewsByPlace={reviewsByPlace} />

      {/* Common complaints */}
      {summary?.complaints?.length > 0 && (
        <div className="card" style={{ padding: 16 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'oklch(0.55 0.16 25)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
            🚨 Keluhan Paling Umum
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            {summary.complaints.map((c, i) => (
              <div key={i} style={{ padding: 12, background: 'oklch(0.97 0.04 25)', borderRadius: 10, borderLeft: '3px solid oklch(0.55 0.16 25)' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6, flexWrap: 'wrap', gap: 6 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: 'oklch(0.42 0.14 25)' }}>{c.theme}</div>
                  <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 999, background: 'rgba(255,255,255,0.7)', color: 'oklch(0.42 0.14 25)', fontWeight: 700 }}>
                    {c.frequency}× disebut
                  </span>
                </div>
                {c.examples?.length > 0 && (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 8 }}>
                    {c.examples.map((ex, j) => (
                      <div key={j} style={{ fontSize: 12, color: 'var(--ink-2)', fontStyle: 'italic', lineHeight: 1.5 }}>
                        "{ex}"
                      </div>
                    ))}
                  </div>
                )}
                {c.action && (
                  <div style={{ fontSize: 12, color: 'var(--ink)', padding: '6px 10px', background: 'rgba(255,255,255,0.6)', borderRadius: 6, lineHeight: 1.5 }}>
                    💡 <b>Action:</b> {c.action}
                  </div>
                )}
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Common praises */}
      {summary?.praises?.length > 0 && (
        <div className="card" style={{ padding: 16 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'oklch(0.42 0.14 155)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
            ✨ Pujian Paling Umum
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {summary.praises.map((p, i) => (
              <div key={i} style={{ padding: '8px 12px', background: 'oklch(0.97 0.04 155)', borderRadius: 8, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'oklch(0.32 0.10 155)' }}>{p.theme}</div>
                <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 999, background: 'rgba(255,255,255,0.7)', color: 'oklch(0.32 0.10 155)', fontWeight: 700 }}>
                  {p.frequency}× disebut
                </span>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* All places ranked + click to see reviews */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', flexWrap: 'wrap', gap: 6 }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
            Semua Outlet ({ranked.length})
          </span>
          <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>
            💬 = ada sample review · diurutkan by rating
          </span>
        </div>
        <div>
          {ranked.map((p) => {
            const isActive = activePlaceId === p.dataId;
            const reviews = reviewsByPlace[p.dataId] || [];
            const hasReviews = reviews.length > 0;
            return (
              <div key={p.dataId}>
                <button
                  onClick={() => setActivePlaceId(isActive ? null : p.dataId)}
                  style={{
                    width: '100%', padding: '12px 16px', textAlign: 'left',
                    background: isActive ? 'var(--bg-2)' : 'var(--bg)',
                    border: 'none', borderTop: '1px solid var(--line-2)', cursor: 'pointer',
                    display: 'grid', gridTemplateColumns: 'auto 1fr auto auto auto', gap: 12, alignItems: 'center',
                  }}
                >
                  <span title={hasReviews ? `${reviews.length} sample review` : 'Tidak di-fetch (bukan outlet paling jelek/bagus)'}
                    style={{
                      fontSize: 11, opacity: hasReviews ? 1 : 0.25,
                      width: 18, textAlign: 'center',
                    }}>{hasReviews ? '💬' : '·'}</span>
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{p.name}</div>
                    <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{p.address}</div>
                  </div>
                  <div className="mono" style={{ fontSize: 14, fontWeight: 700, color: ratingColor(p.rating) }}>
                    {p.rating?.toFixed(1) ?? '—'} ⭐
                  </div>
                  <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', minWidth: 60, textAlign: 'right' }}>
                    {p.reviewsCount.toLocaleString('id-ID')} review
                  </div>
                  <span style={{ fontSize: 10, color: 'var(--ink-4)' }}>{isActive ? '▲' : '▼'}</span>
                </button>
                {isActive && hasReviews && (
                  <OutletReviewsExpand reviews={reviews} />
                )}
                {isActive && !hasReviews && (
                  <div style={{ padding: 14, background: 'var(--bg-2)', fontSize: 12, color: 'var(--ink-3)', lineHeight: 1.5 }}>
                    Outlet ini tidak masuk sample review fetch — kemungkinan brand punya {`>20`} outlet (kita cap di 20 untuk hemat kuota), atau outlet ini punya kurang dari {3} review.
                    Rating outlet ini: <b style={{ color: ratingColor(p.rating) }}>{p.rating?.toFixed(1) ?? '—'} ⭐</b> dari <b>{p.reviewsCount.toLocaleString('id-ID')}</b> review di Google Maps.
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ─── Section: review bintang 1 (komplain mentah) ──────────────────
// Aggregate low-rated reviews cross-outlet, sortable, filterable by keyword.
// Tujuan: kasih user akses langsung ke komentar customer yang komplain
// — komplemen dari AI summary yang sudah generate themes.
function OneStarReviewsSection({ places, reviewsByPlace }) {
  // Diagnostic breakdown: count semua review di sample by rating
  const breakdown = React.useMemo(() => {
    const c = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: 0, outlets: 0 };
    for (const reviews of Object.values(reviewsByPlace || {})) {
      if ((reviews || []).length > 0) c.outlets++;
      for (const rv of (reviews || [])) {
        const r = Math.max(1, Math.min(5, Math.round(rv.rating || 0)));
        c[r] = (c[r] || 0) + 1;
        c.total++;
      }
    }
    return c;
  }, [reviewsByPlace]);

  // Default mode: broader saat ⭐1 kosong supaya user tidak ketemu empty state
  // dulu. Kalau ⭐1 ada, fokus ke yang paling kritis dulu.
  const [maxRating, setMaxRating] = React.useState(() => {
    return breakdown[1] > 0 ? 1 : 2;
  });
  const [search, setSearch] = React.useState('');

  // Re-evaluate default kalau breakdown berubah (e.g. cache load)
  React.useEffect(() => {
    if (breakdown[1] === 0 && maxRating === 1 && breakdown[2] > 0) {
      setMaxRating(2);
    }
  }, [breakdown, maxRating]);

  const allLow = React.useMemo(() => {
    const list = [];
    for (const [dataId, reviews] of Object.entries(reviewsByPlace || {})) {
      const place = (places || []).find((p) => p.dataId === dataId);
      const placeName = place?.name || '?';
      for (const rv of (reviews || [])) {
        if (rv.rating <= maxRating) {
          list.push({ ...rv, store: placeName, dataId });
        }
      }
    }
    list.sort((a, b) => {
      const da = a.isoDate || '';
      const db = b.isoDate || '';
      if (da !== db) return db.localeCompare(da);
      return (a.rating || 0) - (b.rating || 0);
    });
    return list;
  }, [places, reviewsByPlace, maxRating]);

  const q = search.trim().toLowerCase();
  const filtered = q
    ? allLow.filter((rv) =>
        rv.text.toLowerCase().includes(q) || rv.store.toLowerCase().includes(q),
      )
    : allLow;

  // Hanya hide section kalau benar-benar tidak ada review sama sekali
  if (breakdown.total === 0) return null;

  // Group counter per outlet
  const perOutlet = new Map();
  for (const rv of allLow) perOutlet.set(rv.store, (perOutlet.get(rv.store) || 0) + 1);
  const topOutlets = [...perOutlet.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '14px 16px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 200 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
            <span style={{ fontSize: 13, fontWeight: 700, color: 'oklch(0.42 0.14 25)' }}>🚨 Komplain Mentah dari Customer</span>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>
            Breakdown sample {breakdown.total} review dari {breakdown.outlets} outlet:&nbsp;
            <b style={{ color: 'oklch(0.42 0.14 25)' }}>{breakdown[1]}⭐1</b>
            &nbsp;·&nbsp;<b style={{ color: 'oklch(0.55 0.16 25)' }}>{breakdown[2]}⭐2</b>
            &nbsp;·&nbsp;<b style={{ color: 'oklch(0.55 0.16 75)' }}>{breakdown[3]}⭐3</b>
            &nbsp;·&nbsp;<b style={{ color: 'oklch(0.45 0.13 145)' }}>{breakdown[4]}⭐4</b>
            &nbsp;·&nbsp;<b style={{ color: 'oklch(0.42 0.14 155)' }}>{breakdown[5]}⭐5</b>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          {[
            { id: 1, label: '⭐1', count: breakdown[1] },
            { id: 2, label: '⭐1-2', count: breakdown[1] + breakdown[2] },
          ].map((opt) => (
            <button
              key={opt.id}
              onClick={() => setMaxRating(opt.id)}
              disabled={opt.count === 0}
              style={{
                padding: '5px 10px', fontSize: 11.5, fontWeight: 700,
                borderRadius: 6, border: 'none',
                cursor: opt.count === 0 ? 'not-allowed' : 'pointer',
                background: maxRating === opt.id ? 'oklch(0.55 0.16 25)' : 'var(--bg-2)',
                color: maxRating === opt.id ? 'white' : (opt.count === 0 ? 'var(--ink-4)' : 'var(--ink-2)'),
                opacity: opt.count === 0 ? 0.5 : 1,
                display: 'inline-flex', alignItems: 'center', gap: 4,
              }}
            >
              {opt.label}
              <span className="mono" style={{ fontSize: 9.5, opacity: 0.85 }}>{opt.count}</span>
            </button>
          ))}
        </div>
      </div>

      {/* Top outlets dengan komplain terbanyak */}
      {topOutlets.length > 0 && (
        <div style={{ padding: '10px 16px', background: 'oklch(0.97 0.02 25)', borderBottom: '1px solid var(--line)', display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', fontSize: 11.5 }}>
          <span style={{ color: 'var(--ink-3)', fontWeight: 600 }}>Outlet paling banyak dikomplain:</span>
          {topOutlets.map(([name, count], i) => (
            <span key={i} style={{
              padding: '3px 8px', borderRadius: 999,
              background: 'rgba(255,255,255,0.7)', border: '1px solid oklch(0.92 0.05 25)',
              color: 'oklch(0.42 0.14 25)', fontWeight: 600,
            }}>
              {name} <b>×{count}</b>
            </span>
          ))}
        </div>
      )}

      {/* Search bar */}
      <div style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)' }}>
        <input
          className="input"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder='Cari komplain — contoh: "lambat", "ayam keras", "antri"'
          style={{ fontSize: 12.5 }}
        />
      </div>

      {/* Reviews list */}
      <div style={{ maxHeight: 520, overflow: 'auto' }} className="scroll">
        {filtered.length === 0 && q && (
          <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12.5 }}>
            Tidak ada komplain mengandung "<b>{search}</b>".
          </div>
        )}
        {filtered.length === 0 && !q && (
          <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12.5, lineHeight: 1.6 }}>
            {breakdown[1] === 0 && breakdown[2] === 0 ? (
              <>
                🎉 Tidak ada review bintang 1 atau 2 di sample.
                <br/>Brand ini relatif aman dari komplain ekstrem.
                <br/><br/>
                <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>
                  Sample {breakdown.total} review dari {breakdown.outlets} outlet (sort_by=ratingLow).
                  <br/>Kalau di Google Maps kamu lihat banyak ⭐1, kemungkinan outlet itu tidak masuk top-5 worst by rating.
                  <br/>Tip: audit ulang dengan wilayah lebih spesifik (misal pilih kota tertentu) untuk capture outlet jelek di area itu.
                </span>
              </>
            ) : (
              <>
                Tidak ada review <b>{maxRating === 1 ? '⭐1' : '⭐1-2'}</b> di sample.
                <br/>
                {breakdown[1] + breakdown[2] === 0
                  ? <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>Coba klik tombol filter yang ada count {`>`} 0.</span>
                  : <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>Switch ke filter lain di atas.</span>}
              </>
            )}
          </div>
        )}
        {filtered.map((rv, i) => (
          <div key={i} style={{
            padding: '12px 16px',
            borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
          }}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
              <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: 'oklch(0.55 0.16 25)' }}>
                {'⭐'.repeat(rv.rating)} {rv.rating}/5
              </span>
              <span style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--ink-2)' }}>{rv.store}</span>
              <span style={{ fontSize: 10.5, color: 'var(--ink-4)', marginLeft: 'auto' }}>
                {rv.date} · {rv.user || 'Anonim'}
              </span>
            </div>
            <div style={{ fontSize: 13, color: 'var(--ink)', lineHeight: 1.55 }}>
              {q ? highlightMatch(rv.text, q) : rv.text}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ageStrAudit(ms) {
  if (ms == null || ms < 60_000) return 'baru saja';
  const min = Math.floor(ms / 60_000);
  if (min < 60) return `${min} mnt lalu`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr} jam lalu`;
  return `${Math.floor(hr / 24)} hari lalu`;
}

function highlightMatch(text, q) {
  const idx = text.toLowerCase().indexOf(q);
  if (idx < 0) return text;
  return (
    <>
      {text.slice(0, idx)}
      <mark style={{ background: 'oklch(0.96 0.10 75)', padding: '0 2px', borderRadius: 3 }}>
        {text.slice(idx, idx + q.length)}
      </mark>
      {text.slice(idx + q.length)}
    </>
  );
}

// ─── Per-outlet expand: default fokus bintang 1 ───────────────────
// Saat user klik outlet, langsung filter ke komplain bintang 1 dulu —
// itu yang paling actionable untuk perbaikan. Toggle untuk broaden.
function OutletReviewsExpand({ reviews }) {
  // Filter mode: 1 | 2 | 'all'. Default 1 (bintang 1 saja).
  const [mode, setMode] = React.useState(1);

  const counts = React.useMemo(() => {
    const c = { 1: 0, 2: 0, all: reviews.length };
    for (const rv of reviews) {
      if (rv.rating === 1) c[1]++;
      if (rv.rating <= 2) c[2]++;
    }
    return c;
  }, [reviews]);

  const filtered = React.useMemo(() => {
    if (mode === 'all') return reviews;
    if (mode === 1) return reviews.filter((rv) => rv.rating === 1);
    return reviews.filter((rv) => rv.rating <= 2);
  }, [reviews, mode]);

  // Auto-broaden kalau filter awal kosong: prefer level berikutnya yang ada isi
  React.useEffect(() => {
    if (mode === 1 && counts[1] === 0 && counts[2] > 0) setMode(2);
    else if (mode === 2 && counts[2] === 0 && counts.all > 0) setMode('all');
  }, [counts, mode]);

  return (
    <div style={{ padding: '10px 16px 14px', background: 'var(--bg-2)' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8, flexWrap: 'wrap', gap: 6 }}>
        <div style={{ fontSize: 10.5, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em', fontWeight: 600 }}>
          🚨 Komplain & Komentar
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          {[
            { id: 1,     label: '⭐1',    color: 'oklch(0.55 0.16 25)' },
            { id: 2,     label: '⭐1-2',  color: 'oklch(0.55 0.16 75)' },
            { id: 'all', label: 'Semua', color: 'var(--primary)' },
          ].map((opt) => (
            <button
              key={opt.id}
              onClick={() => setMode(opt.id)}
              disabled={counts[opt.id] === 0}
              style={{
                padding: '4px 9px', fontSize: 11, fontWeight: 700,
                borderRadius: 6, border: 'none', cursor: counts[opt.id] === 0 ? 'not-allowed' : 'pointer',
                background: mode === opt.id ? opt.color : 'var(--bg)',
                color: mode === opt.id ? 'white' : (counts[opt.id] === 0 ? 'var(--ink-4)' : 'var(--ink-2)'),
                opacity: counts[opt.id] === 0 ? 0.5 : 1,
                display: 'inline-flex', alignItems: 'center', gap: 4,
              }}
            >
              {opt.label}
              <span className="mono" style={{ fontSize: 9.5, opacity: 0.85 }}>{counts[opt.id]}</span>
            </button>
          ))}
        </div>
      </div>

      {filtered.length === 0 && (
        <div style={{ padding: 16, fontSize: 12, color: 'var(--ink-3)', background: 'var(--bg)', borderRadius: 8, textAlign: 'center', lineHeight: 1.55 }}>
          {mode === 1
            ? <>🎉 Tidak ada review <b>bintang 1</b> di sample outlet ini.</>
            : mode === 2
            ? <>Tidak ada review <b>bintang 1-2</b> di sample.</>
            : 'Tidak ada review.'}
        </div>
      )}

      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {filtered.map((rv, i) => (
          <div key={i} style={{
            padding: '8px 10px', background: 'var(--bg)', borderRadius: 6,
            borderLeft: `3px solid ${ratingColor(rv.rating)}`,
          }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 3 }}>
              <span className="mono" style={{ fontSize: 11.5, fontWeight: 700, color: ratingColor(rv.rating) }}>
                {'⭐'.repeat(rv.rating)} {rv.rating}/5
              </span>
              <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{rv.date} · {rv.user || 'Anonim'}</span>
            </div>
            <div style={{ fontSize: 12.5, color: 'var(--ink)', lineHeight: 1.55 }}>{rv.text}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ratingColor(r) {
  if (r == null) return 'var(--ink-3)';
  if (r >= 4.5) return 'oklch(0.42 0.14 155)';
  if (r >= 4.0) return 'oklch(0.45 0.13 145)';
  if (r >= 3.5) return 'oklch(0.55 0.16 75)';
  return 'oklch(0.55 0.16 25)';
}

function StoreRow({ store, positive }) {
  return (
    <div style={{ padding: 10, background: positive ? 'oklch(0.97 0.04 155)' : 'oklch(0.97 0.04 25)', borderRadius: 8 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 3 }}>
        <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--ink)' }}>{store.name}</span>
        <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: positive ? 'oklch(0.42 0.14 155)' : 'oklch(0.55 0.16 25)' }}>
          {store.rating?.toFixed(1) ?? '—'} ⭐
        </span>
      </div>
      <div style={{ fontSize: 11, color: 'var(--ink-3)', marginBottom: 4 }}>{store.reviews?.toLocaleString('id-ID')} review</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-2)', lineHeight: 1.5 }}>{store.reason}</div>
    </div>
  );
}

function SummaryStat({ label, value, unit }) {
  return (
    <div style={{ padding: '6px 12px', background: 'var(--bg-2)', borderRadius: 8, textAlign: 'right' }}>
      <div style={{ fontSize: 10, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em', fontWeight: 600 }}>{label}</div>
      <div className="mono" style={{ fontSize: 18, fontWeight: 700, color: 'var(--ink)', letterSpacing: '-0.01em' }}>
        {value} {unit && <span style={{ fontSize: 12, marginLeft: 2 }}>{unit}</span>}
      </div>
    </div>
  );
}

// ─── Tab: Dashboard — analyst-grade overview ────────────────────────
function SuperAdminAuditDashboard() {
  const useState_dh = React.useState;
  const useEffect_dh = React.useEffect;
  const [data, setData] = useState_dh(null);
  const [loading, setLoading] = useState_dh(true);
  const [error, setError] = useState_dh(null);

  const reload = () => {
    setLoading(true); setError(null);
    window.api.auditDashboard()
      .then(setData)
      .catch((e) => setError(e.message || 'Gagal memuat dashboard'))
      .finally(() => setLoading(false));
  };
  useEffect_dh(() => { reload(); }, []);

  if (loading) {
    return <div style={{ padding: 40, textAlign: 'center', color: 'var(--ink-3)' }}>Memuat dashboard analytics…</div>;
  }
  if (error) {
    return <div style={{ padding: 12, borderRadius: 10, background: 'var(--danger-soft)', color: 'var(--danger)', fontSize: 13 }}>{error}</div>;
  }
  if (!data) return null;

  const { stats, ratingDistribution, actionItems, recentAudits, topComplaintsGlobal, topPraisesGlobal, topOutlets, worstOutlets, byRegion, trends } = data;

  // Empty state — no audits yet
  if (stats.auditsTotal === 0) {
    return (
      <div className="card" style={{ padding: 48, textAlign: 'center' }}>
        <div style={{ fontSize: 40, marginBottom: 10 }}>📊</div>
        <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 6 }}>Dashboard masih kosong</div>
        <div style={{ fontSize: 13, color: 'var(--ink-3)', maxWidth: 460, margin: '0 auto', lineHeight: 1.55 }}>
          Belum ada brand audit yang ke-cache. Buka tab <b>Brand Audit</b> untuk scan brand pertama kamu, lalu kembali ke sini untuk lihat overview lengkap.
        </div>
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {/* ─── Executive Header ─────────────────────────────────── */}
      <ExecutiveHeader stats={stats} onRefresh={reload} />

      {/* ─── Action Items (paling priority) ───────────────────── */}
      {actionItems.length > 0 && <ActionItemsPanel items={actionItems} />}

      {/* ─── Rating Distribution + Trend per Brand (2-col) ─── */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: 14 }}>
        <RatingDistributionCard distribution={ratingDistribution} total={stats.ratingDistTotal} />
        <TrendsTimelineCard trends={trends} />
      </div>

      {/* ─── Complaints + Praises (2-col) ───────────────────── */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <ThemesCard
          title="🚨 Pain-Points Lintas Brand"
          subtitle="Keluhan yang paling sering disebut customer di Google Maps"
          items={topComplaintsGlobal}
          colorBg="oklch(0.97 0.04 25)"
          colorFg="oklch(0.42 0.14 25)"
          colorAccent="oklch(0.55 0.16 25)"
        />
        <ThemesCard
          title="✨ Pola yang Berhasil"
          subtitle="Pujian customer yang paling sering disebut — pelajaran untuk replicate"
          items={topPraisesGlobal}
          colorBg="oklch(0.97 0.04 155)"
          colorFg="oklch(0.32 0.10 155)"
          colorAccent="oklch(0.42 0.14 155)"
        />
      </div>

      {/* ─── Top vs Worst Outlets (2-col) ───────────────────── */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <OutletsTable
          title="🏆 Outlet Champion"
          subtitle="Rating tertinggi lintas brand — pelajari SOP-nya untuk replicate"
          outlets={topOutlets}
          positive
        />
        <OutletsTable
          title="⚠️ Outlet Butuh Intervensi"
          subtitle="Rating terendah lintas brand — prioritas tindak lanjut"
          outlets={worstOutlets}
          positive={false}
        />
      </div>

      {/* ─── Geographic Breakdown ─────────────────────────────── */}
      {byRegion.length > 0 && <RegionBreakdownCard regions={byRegion} />}

      {/* ─── Trend monitoring detail (per-brand cards) ────────── */}
      {trends.length > 0 && <TrendDetailSection trends={trends} />}

      {/* ─── Recent audits ──────────────────────────────────────── */}
      <RecentAuditsCard audits={recentAudits} onRefresh={reload} />
    </div>
  );
}

// ─── Executive Header — health score + KPIs ───────────────────────
function ExecutiveHeader({ stats, onRefresh }) {
  const health = stats.healthScore;
  const healthColor = health == null ? 'var(--ink-3)'
    : health >= 4.5 ? 'oklch(0.42 0.14 155)'
    : health >= 4.0 ? 'oklch(0.45 0.13 145)'
    : health >= 3.5 ? 'oklch(0.55 0.16 75)'
    : 'oklch(0.55 0.16 25)';
  const healthLabel = health == null ? '—'
    : health >= 4.5 ? 'EXCELLENT'
    : health >= 4.0 ? 'GOOD'
    : health >= 3.5 ? 'FAIR'
    : 'NEEDS WORK';
  const quotaPct = stats.quota.limit > 0 ? Math.round((stats.quota.used / stats.quota.limit) * 100) : 0;

  return (
    <div className="card" style={{
      padding: 0, overflow: 'hidden',
      background: 'linear-gradient(135deg, var(--surface) 0%, var(--bg-2) 100%)',
    }}>
      <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10 }}>
        <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Executive Summary</span>
        <span style={{ fontSize: 11, color: 'var(--ink-4)' }}>· Brand Audit Overview</span>
        <a
          href="/api/admin/brand-audit/dashboard/report"
          target="_blank"
          rel="noreferrer"
          className="btn btn-primary"
          style={{ marginLeft: 'auto', padding: '5px 12px', fontSize: 11.5, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 5 }}
          title="Generate PDF presentasi portfolio (multi-brand) dengan AI narrative"
        >
          🖨️ Print Dashboard PDF
        </a>
        <button onClick={onRefresh} className="btn btn-ghost" style={{ padding: '4px 10px', fontSize: 11.5 }}>
          ↻ Refresh
        </button>
      </div>
      <div style={{ padding: 22, display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 24, alignItems: 'center' }}>
        {/* Health Score (gauge-style) */}
        <div style={{ textAlign: 'center', minWidth: 160 }}>
          <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>
            Brand Health Score
          </div>
          <div className="mono" style={{ fontSize: 56, fontWeight: 800, color: healthColor, lineHeight: 1, letterSpacing: '-0.03em' }}>
            {health?.toFixed(1) ?? '—'}
          </div>
          <div style={{ fontSize: 10.5, fontWeight: 700, color: healthColor, textTransform: 'uppercase', letterSpacing: '0.06em', marginTop: 6 }}>
            {healthLabel} <span style={{ color: 'var(--ink-4)' }}>· /5.0</span>
          </div>
        </div>

        {/* KPIs grid */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: 14 }}>
          <KPI icon="🏪" label="Brand" value={stats.brandsTracked} />
          <KPI icon="📋" label="Audit" value={stats.auditsTotal} />
          <KPI icon="📍" label="Outlet" value={stats.outletsTracked} />
          <KPI icon="💬" label="Review" value={(stats.reviewsAggregated || 0).toLocaleString('id-ID')} />
          <KPI
            icon="⚡" label={`Quota ${stats.quota.yearMonth}`}
            value={`${stats.quota.used}/${stats.quota.limit}`}
            sub={`${quotaPct}%`}
            warn={quotaPct >= 80}
          />
        </div>
      </div>
    </div>
  );
}

function KPI({ icon, label, value, sub, warn }) {
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginBottom: 4 }}>
        <span style={{ fontSize: 13 }}>{icon}</span>
        <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
          {label}
        </span>
      </div>
      <div className="mono" style={{ fontSize: 22, fontWeight: 800, color: warn ? 'oklch(0.55 0.16 25)' : 'var(--ink)', letterSpacing: '-0.02em', lineHeight: 1 }}>
        {value}
      </div>
      {sub && <div style={{ fontSize: 10.5, color: warn ? 'oklch(0.55 0.16 25)' : 'var(--ink-4)', marginTop: 3, fontWeight: 600 }}>{sub}</div>}
    </div>
  );
}

// ─── Action Items panel ─────────────────────────────────────────
function ActionItemsPanel({ items }) {
  const sevColor = (s) => s === "high" ? 'oklch(0.55 0.16 25)' : s === "med" ? 'oklch(0.55 0.16 75)' : 'oklch(0.5 0.13 230)';
  const sevBg = (s) => s === "high" ? 'oklch(0.96 0.05 25)' : s === "med" ? 'oklch(0.96 0.05 75)' : 'oklch(0.96 0.04 230)';
  const sevLabel = (s) => s === "high" ? 'CRITICAL' : s === "med" ? 'WARNING' : 'INFO';
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden', borderColor: 'oklch(0.55 0.16 25 / 0.3)' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)', background: 'oklch(0.97 0.04 25)' }}>
        <div style={{ fontSize: 13, fontWeight: 700, color: 'oklch(0.42 0.14 25)' }}>🚨 Action Required ({items.length})</div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>
          Insight prioritas dari analisis brand health — actionable next step
        </div>
      </div>
      <div>
        {items.map((it, i) => (
          <div key={i} style={{
            padding: '12px 16px',
            borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
            display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 12, alignItems: 'flex-start',
          }}>
            <span style={{
              fontSize: 9.5, padding: '3px 8px', borderRadius: 4, fontWeight: 800,
              background: sevBg(it.severity), color: sevColor(it.severity),
              letterSpacing: '0.06em', minWidth: 64, textAlign: 'center',
            }}>{sevLabel(it.severity)}</span>
            <div>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink)' }}>{it.title}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-2)', marginTop: 2, lineHeight: 1.5 }}>{it.detail}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Rating Distribution (horizontal stacked bars) ──────────────
function RatingDistributionCard({ distribution, total }) {
  const colors = {
    5: 'oklch(0.42 0.14 155)',
    4: 'oklch(0.45 0.13 145)',
    3: 'oklch(0.55 0.16 75)',
    2: 'oklch(0.55 0.16 25)',
    1: 'oklch(0.42 0.14 25)',
  };
  const order = [5, 4, 3, 2, 1];
  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>📊 Distribusi Rating</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 14 }}>
        Breakdown {total.toLocaleString('id-ID')} review yang ke-cache di sample
      </div>

      {total === 0 ? (
        <div style={{ fontSize: 12, color: 'var(--ink-3)', padding: 16, textAlign: 'center' }}>
          Belum ada review — audit brand untuk mulai capture review.
        </div>
      ) : (
        <>
          {/* Stacked horizontal bar */}
          <div style={{ display: 'flex', height: 28, borderRadius: 8, overflow: 'hidden', marginBottom: 14, border: '1px solid var(--line)' }}>
            {order.map((r) => {
              const count = distribution[r] || 0;
              const pct = total > 0 ? (count / total) * 100 : 0;
              if (pct === 0) return null;
              return (
                <div key={r} title={`${r}⭐: ${count} (${pct.toFixed(1)}%)`} style={{
                  width: `${pct}%`, background: colors[r],
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  color: 'white', fontSize: 10, fontWeight: 700,
                }}>
                  {pct >= 8 ? `${pct.toFixed(0)}%` : ''}
                </div>
              );
            })}
          </div>

          {/* Per-rating row */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {order.map((r) => {
              const count = distribution[r] || 0;
              const pct = total > 0 ? (count / total) * 100 : 0;
              return (
                <div key={r} style={{ display: 'grid', gridTemplateColumns: '50px 1fr auto', gap: 10, alignItems: 'center', fontSize: 12 }}>
                  <span className="mono" style={{ color: colors[r], fontWeight: 700 }}>{'⭐'.repeat(r)} {r}</span>
                  <div style={{ height: 6, borderRadius: 999, background: 'var(--bg-2)', overflow: 'hidden' }}>
                    <div style={{ width: `${pct}%`, height: '100%', background: colors[r], borderRadius: 999 }} />
                  </div>
                  <span className="mono" style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600, minWidth: 70, textAlign: 'right' }}>
                    {count.toLocaleString('id-ID')} <span style={{ color: 'var(--ink-4)' }}>({pct.toFixed(1)}%)</span>
                  </span>
                </div>
              );
            })}
          </div>
        </>
      )}
    </div>
  );
}

// ─── Trends Timeline (multi-brand SVG line chart) ──────────────
function TrendsTimelineCard({ trends }) {
  const withMultiSnap = trends.filter((t) => t.timeline.length >= 2);
  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>📈 Trajectory Rating</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 14 }}>
        {withMultiSnap.length === 0
          ? 'Audit brand 2× atau lebih untuk lihat trajectory rating dari waktu ke waktu'
          : `Riwayat rating ${withMultiSnap.length} brand — sumbu Y = avg rating, X = waktu audit`}
      </div>
      {withMultiSnap.length === 0 ? (
        <div style={{ padding: 28, textAlign: 'center', color: 'var(--ink-4)', fontSize: 12.5 }}>
          🕒 Audit lagi 1-2 minggu kemudian untuk lihat trajectory
        </div>
      ) : (
        <TrajectoryChart trends={withMultiSnap.slice(0, 5)} />
      )}
    </div>
  );
}

function TrajectoryChart({ trends }) {
  const W = 600, H = 200, PAD = { top: 14, right: 90, bottom: 28, left: 30 };
  const innerW = W - PAD.left - PAD.right;
  const innerH = H - PAD.top - PAD.bottom;
  const palette = ['var(--primary)', 'oklch(0.55 0.16 25)', 'oklch(0.55 0.14 145)', 'oklch(0.62 0.14 230)', 'oklch(0.55 0.16 290)'];

  // Find global x range (oldest to newest across all trends)
  let minTime = Infinity, maxTime = -Infinity;
  for (const t of trends) {
    for (const p of t.timeline) {
      const ms = new Date(p.at).getTime();
      if (ms < minTime) minTime = ms;
      if (ms > maxTime) maxTime = ms;
    }
  }
  const span = Math.max(1, maxTime - minTime);
  const xAt = (ms) => PAD.left + ((ms - minTime) / span) * innerW;
  const yAt = (v) => PAD.top + (1 - (v - 1) / 4) * innerH; // rating 1..5

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
      {/* Y gridlines: 1, 2, 3, 4, 5 */}
      {[1, 2, 3, 4, 5].map((v) => (
        <g key={v}>
          <line x1={PAD.left} y1={yAt(v)} x2={W - PAD.right} y2={yAt(v)} stroke="var(--line-2)" strokeWidth="0.5" />
          <text x={PAD.left - 6} y={yAt(v) + 3} fontSize="9" fill="var(--ink-3)" textAnchor="end">{v}</text>
        </g>
      ))}
      {/* Lines */}
      {trends.map((t, i) => {
        const color = palette[i % palette.length];
        const points = t.timeline.filter((p) => p.rating != null).map((p) => [xAt(new Date(p.at).getTime()), yAt(p.rating)]);
        if (points.length === 0) return null;
        const path = points.map((p, j) => `${j === 0 ? 'M' : 'L'} ${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
        const last = points[points.length - 1];
        return (
          <g key={i}>
            <path d={path} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
            {points.map((p, j) => (
              <circle key={j} cx={p[0]} cy={p[1]} r="3" fill={color} stroke="white" strokeWidth="1.5" />
            ))}
            <text x={last[0] + 8} y={last[1] + 3.5} fontSize="10" fill={color} fontWeight={700}>
              {t.brandKey.length > 12 ? t.brandKey.slice(0, 11) + '…' : t.brandKey}
            </text>
          </g>
        );
      })}
      {/* X axis: dates */}
      <text x={PAD.left} y={H - 6} fontSize="9" fill="var(--ink-3)" textAnchor="start">
        {new Date(minTime).toLocaleDateString('id-ID', { day: '2-digit', month: 'short' })}
      </text>
      <text x={W - PAD.right} y={H - 6} fontSize="9" fill="var(--ink-3)" textAnchor="end">
        {new Date(maxTime).toLocaleDateString('id-ID', { day: '2-digit', month: 'short' })}
      </text>
    </svg>
  );
}

// ─── Themes card (complaints / praises) ──────────────────────────
function ThemesCard({ title, subtitle, items, colorBg, colorFg, colorAccent }) {
  if (items.length === 0) return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>{title}</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 12 }}>{subtitle}</div>
      <div style={{ fontSize: 12, color: 'var(--ink-4)', textAlign: 'center', padding: 16 }}>
        Belum ada tema teridentifikasi — audit lebih banyak brand untuk dapat data.
      </div>
    </div>
  );
  const max = items[0]?.frequency || 1;
  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>{title}</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 12 }}>{subtitle}</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        {items.map((c, i) => {
          const pct = (c.frequency / max) * 100;
          return (
            <div key={i} style={{ display: 'grid', gridTemplateColumns: '20px 1fr auto', gap: 10, alignItems: 'center', padding: '7px 10px', background: colorBg, borderRadius: 8 }}>
              <span className="mono" style={{ fontSize: 11, fontWeight: 700, color: colorFg }}>#{i + 1}</span>
              <div>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink)', marginBottom: 3 }}>{c.theme}</div>
                <div style={{ height: 4, borderRadius: 999, background: 'rgba(255,255,255,0.5)', overflow: 'hidden' }}>
                  <div style={{ width: `${pct}%`, height: '100%', background: colorAccent, borderRadius: 999 }} />
                </div>
              </div>
              <span className="mono" style={{ fontSize: 11, padding: '2px 7px', borderRadius: 999, background: 'rgba(255,255,255,0.7)', color: colorFg, fontWeight: 700 }}>
                {c.frequency}×
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Outlets table (top / worst) ────────────────────────────────
function OutletsTable({ title, subtitle, outlets, positive }) {
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)' }}>
        <div style={{ fontSize: 13, fontWeight: 700 }}>{title}</div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>{subtitle}</div>
      </div>
      {outlets.length === 0 ? (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-4)', fontSize: 12 }}>
          Belum ada outlet yang memenuhi kriteria.
        </div>
      ) : (
        <div>
          {outlets.map((o, i) => (
            <div key={i} style={{
              padding: '10px 16px',
              borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
              display: 'grid', gridTemplateColumns: '20px 1fr auto auto', gap: 10, alignItems: 'center',
              fontSize: 12.5,
              background: positive && i === 0 ? 'oklch(0.97 0.04 155)' : (!positive && i === 0 ? 'oklch(0.97 0.04 25)' : 'var(--bg)'),
            }}>
              <span className="mono" style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)' }}>{i + 1}</span>
              <div>
                <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{o.name.length > 28 ? o.name.slice(0, 27) + '…' : o.name}</div>
                <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>
                  {o.brand} · {o.region}
                </div>
              </div>
              <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: ratingColor(o.rating) }}>
                {o.rating?.toFixed(1)} ⭐
              </div>
              <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>
                {o.reviewsCount.toLocaleString('id-ID')}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Region breakdown ───────────────────────────────────────────
function RegionBreakdownCard({ regions }) {
  const maxOutlets = Math.max(1, ...regions.map((r) => r.outlets));
  return (
    <div className="card" style={{ padding: 18 }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>🗺️ Sebaran per Wilayah</div>
      <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 14 }}>
        Coverage audit per region — outlet count + rata-rata rating
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {regions.map((r, i) => {
          const pct = (r.outlets / maxOutlets) * 100;
          return (
            <div key={i} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto auto', gap: 12, alignItems: 'center', fontSize: 12.5 }}>
              <span style={{ fontWeight: 600, color: 'var(--ink-2)' }}>{r.region}</span>
              <div style={{ height: 7, borderRadius: 999, background: 'var(--bg-2)', overflow: 'hidden' }}>
                <div style={{ width: `${pct}%`, height: '100%', background: 'var(--primary)', borderRadius: 999 }} />
              </div>
              <span className="mono" style={{ fontSize: 11.5, color: 'var(--ink-3)', minWidth: 60, textAlign: 'right' }}>
                {r.outlets} outlet
              </span>
              <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: ratingColor(r.avgRating), minWidth: 50, textAlign: 'right' }}>
                {r.avgRating?.toFixed(1) ?? '—'}⭐
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Trend detail (per-brand cards) ─────────────────────────────
function TrendDetailSection({ trends }) {
  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)' }}>
        <div style={{ fontSize: 13, fontWeight: 700 }}>📉 Detail Trend per Brand</div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>
          Disorted by drop terbesar dulu — prioritas alert
        </div>
      </div>
      <div>
        {trends.map((t) => {
          const noPrev = t.previousRating == null;
          const positive = (t.ratingDelta ?? 0) >= 0;
          const big = Math.abs(t.ratingDelta ?? 0) >= 0.2;
          return (
            <div key={`${t.brandKey}__${t.locationKey}`} style={{
              padding: '12px 16px', borderTop: '1px solid var(--line-2)',
              display: 'grid', gridTemplateColumns: '1.4fr auto auto 100px 1fr', gap: 12, alignItems: 'center', fontSize: 12.5,
            }}>
              <div>
                <div style={{ fontWeight: 700, color: 'var(--ink)' }}>{t.brandKey}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{t.region}</div>
              </div>
              <div style={{ textAlign: 'center' }}>
                <div style={{ fontSize: 9.5, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em', fontWeight: 700 }}>Sebelumnya</div>
                <div className="mono" style={{ fontSize: 14, fontWeight: 700, color: ratingColor(t.previousRating) }}>{t.previousRating?.toFixed(1) ?? '—'}</div>
              </div>
              <div style={{ textAlign: 'center' }}>
                <div style={{ fontSize: 9.5, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em', fontWeight: 700 }}>Sekarang</div>
                <div className="mono" style={{ fontSize: 14, fontWeight: 700, color: ratingColor(t.latestRating) }}>{t.latestRating?.toFixed(1) ?? '—'}</div>
              </div>
              <div style={{ textAlign: 'center' }}>
                {noPrev ? (
                  <span style={{ fontSize: 10, padding: '3px 8px', borderRadius: 999, background: 'var(--bg-2)', color: 'var(--ink-3)', fontWeight: 600 }}>
                    1× snapshot
                  </span>
                ) : (
                  <>
                    <span className="mono" style={{
                      fontSize: 13, fontWeight: 800,
                      padding: '4px 9px', borderRadius: 8,
                      background: positive ? 'oklch(0.96 0.05 155)' : 'oklch(0.96 0.05 25)',
                      color: positive ? 'oklch(0.42 0.14 155)' : 'oklch(0.55 0.16 25)',
                    }}>
                      {positive ? '↑' : '↓'} {(positive ? '+' : '')}{t.ratingDelta?.toFixed(2)}
                      {big && ' ⚠'}
                    </span>
                    <div style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 3 }}>{t.daysBetween ?? 0}d ago</div>
                  </>
                )}
              </div>
              {/* Mini sparkline */}
              <Sparkline timeline={t.timeline} />
            </div>
          );
        })}
      </div>
    </div>
  );
}

function Sparkline({ timeline }) {
  if (!timeline || timeline.length === 0) {
    return <div style={{ fontSize: 10, color: 'var(--ink-4)' }}>—</div>;
  }
  const W = 110, H = 28;
  const ratings = timeline.map((p) => p.rating).filter((r) => r != null);
  if (ratings.length < 2) {
    return <div style={{ fontSize: 10, color: 'var(--ink-4)', textAlign: 'right' }}>—</div>;
  }
  const min = Math.min(...ratings) - 0.2;
  const max = Math.max(...ratings) + 0.2;
  const span = Math.max(0.4, max - min);
  const xAt = (i) => (i / (timeline.length - 1)) * (W - 4) + 2;
  const yAt = (v) => H - 4 - ((v - min) / span) * (H - 8);
  const path = timeline.map((p, i) => p.rating == null ? null : [xAt(i), yAt(p.rating)]).filter(Boolean);
  if (path.length < 2) return <div style={{ fontSize: 10, color: 'var(--ink-4)' }}>—</div>;
  const lastRating = ratings[ratings.length - 1];
  const firstRating = ratings[0];
  const trendUp = lastRating >= firstRating;
  const color = trendUp ? 'oklch(0.42 0.14 155)' : 'oklch(0.55 0.16 25)';
  return (
    <svg width={W} height={H}>
      <path
        d={path.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ')}
        fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
      />
      <circle cx={path[path.length - 1][0]} cy={path[path.length - 1][1]} r="2" fill={color} />
    </svg>
  );
}

// ─── Recent audits log ──────────────────────────────────────────
function RecentAuditsCard({ audits, onRefresh }) {
  const [deletingKey, setDeletingKey] = React.useState(null);

  const handleDelete = async (a) => {
    if (!confirm(`Hapus audit "${a.brandKey}" di ${a.region}?\n\nSemua snapshot history brand ini juga akan dihapus.\nReview per outlet di tab Komentar Toko TIDAK terpengaruh.`)) return;
    const key = `${a.brandKey}__${a.locationKey}`;
    setDeletingKey(key);
    try {
      await window.api.deleteBrandAudit(a.brandKey, a.locationKey);
      onRefresh();
    } catch (e) {
      alert('Gagal menghapus: ' + (e.message || 'unknown'));
    } finally {
      setDeletingKey(null);
    }
  };

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span style={{ fontSize: 13, fontWeight: 700 }}>📋 Audit Terkini</span>
        <button onClick={onRefresh} className="btn btn-ghost" style={{ padding: '4px 10px', fontSize: 11.5 }}>
          ↻ Refresh
        </button>
      </div>
      {audits.length === 0 ? (
        <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
          Belum ada audit. Buka tab <b>Brand Audit</b> untuk mulai.
        </div>
      ) : (
        <div>
          {audits.map((a, i) => {
            const key = `${a.brandKey}__${a.locationKey}`;
            const deleting = deletingKey === key;
            return (
              <div key={i} style={{
                padding: '10px 16px',
                borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
                display: 'grid', gridTemplateColumns: '1.5fr 1.5fr auto auto auto auto auto', gap: 8, alignItems: 'center', fontSize: 12.5,
                opacity: deleting ? 0.4 : 1,
              }}>
                <div>
                  <div style={{ fontWeight: 700, color: 'var(--ink)' }}>{a.brandKey}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{a.region}</div>
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-2)' }}>
                  {a.topComplaint
                    ? <>Top complaint: <b>{a.topComplaint}</b></>
                    : <span style={{ color: 'var(--ink-4)' }}>—</span>}
                </div>
                <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: ratingColor(a.overallRating) }}>
                  {a.overallRating?.toFixed(1) ?? '—'} ⭐
                </div>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>
                  {a.placeCount} outlet · {a.searchesUsed} search
                </div>
                <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>
                  {ageStrAudit(Date.now() - new Date(a.generatedAt).getTime())}
                </div>
                <a
                  href={`/api/admin/brand-audit/${encodeURIComponent(a.brandKey)}/${encodeURIComponent(a.locationKey)}/report`}
                  target="_blank"
                  rel="noreferrer"
                  title="Generate PDF presentasi (AI narrative + infografik)"
                  style={{
                    background: 'transparent', border: '1px solid var(--line)',
                    padding: '4px 7px', borderRadius: 6,
                    cursor: 'pointer', color: 'var(--ink-3)', fontSize: 12,
                    transition: 'all 120ms', textDecoration: 'none',
                  }}
                  onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--primary-50)'; e.currentTarget.style.borderColor = 'var(--primary)'; e.currentTarget.style.color = 'var(--primary)'; }}
                  onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                >📄</a>
                <button
                  onClick={() => handleDelete(a)}
                  disabled={deleting}
                  title="Hapus audit (snapshot history juga ke-clean)"
                  style={{
                    background: 'transparent', border: '1px solid var(--line)',
                    padding: '4px 7px', borderRadius: 6,
                    cursor: deleting ? 'wait' : 'pointer',
                    color: 'var(--ink-3)', fontSize: 12,
                    transition: 'all 120ms',
                  }}
                  onMouseEnter={(e) => { if (!deleting) { e.currentTarget.style.background = 'var(--danger-soft)'; e.currentTarget.style.borderColor = 'var(--danger)'; e.currentTarget.style.color = 'var(--danger)'; } }}
                  onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                >🗑</button>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ─── Tab: Komentar Toko ────────────────────────────────────────────
// Step 1: input brand+region → cari outlet (1-3 SerpAPI)
// Step 2: list outlet → klik 1 outlet
// Step 3: fetch review outlet itu (2 SerpAPI dual-fetch) → tampilkan
function SuperAdminOutletReviews() {
  const useState_or = React.useState;
  const useEffect_or = React.useEffect;
  const [brand, setBrand] = useState_or('');
  const [regionKey, setRegionKey] = useState_or('indonesia');
  const [regions, setRegions] = useState_or([]);
  const [places, setPlaces] = useState_or([]);
  const [placesCached, setPlacesCached] = useState_or(false);
  const [placesAgeMs, setPlacesAgeMs] = useState_or(0);
  const [searchingPlaces, setSearchingPlaces] = useState_or(false);
  const [activePlace, setActivePlace] = useState_or(null);
  const [reviewsData, setReviewsData] = useState_or(null);
  const [reviewsLoading, setReviewsLoading] = useState_or(false);
  const [reviewsCachedAt, setReviewsCachedAt] = useState_or(null);
  const [error, setError] = useState_or(null);

  useEffect_or(() => {
    window.api.listAuditRegions().then((r) => setRegions(r.regions || [])).catch(() => {});
  }, []);

  const findPlaces = async (force) => {
    if (!brand.trim()) return;
    setSearchingPlaces(true); setError(null); setActivePlace(null); setReviewsData(null);
    try {
      const r = await window.api.findPlaces({ brand: brand.trim(), region: regionKey, force: !!force });
      setPlaces(r.places || []);
      setPlacesCached(!!r.cached);
      setPlacesAgeMs(r.ageMs || 0);
    } catch (e) {
      setError(e.message || 'Gagal cari outlet');
    } finally {
      setSearchingPlaces(false);
    }
  };

  // mode: 'cache' (read-only, no SerpAPI) | 'update' (incremental, 1 SerpAPI) | 'full' (re-scan, 3 SerpAPI)
  const openOutlet = async (place, mode = 'cache') => {
    setActivePlace(place);
    setReviewsData(null);
    setError(null);

    if (mode === 'cache') {
      setReviewsLoading(true);
      try {
        const r = await window.api.getOutletReviews(place.dataId);
        if (r.cached && r.data) {
          setReviewsData(r.data);
          setReviewsCachedAt(r.ageMs);
          setReviewsLoading(false);
          return;
        }
        // Belum ada di DB → fall-through ke fetch (akan jadi full kalau pertama kali)
      } catch {}
    }

    setReviewsLoading(true);
    try {
      const ctx = {
        name: place.name,
        address: place.address,
        rating: place.rating,
        reviewsCount: place.reviewsCount,
      };
      const r = mode === 'full'
        ? await window.api.rescanOutletReviews(place.dataId, ctx)
        : await window.api.fetchOutletReviews(place.dataId, ctx); // default: update mode (incremental)
      setReviewsData(r.data);
      setReviewsCachedAt(0);
      // Show toast-style message kalau ada review baru
      if (r.newReviewsAdded > 0 && mode !== 'cache') {
        setTimeout(() => alert(`✓ ${r.newReviewsAdded} review baru ditambahkan ke database (mode: ${r.mode === 'full' ? 'Re-scan penuh' : 'Update terbaru'}).`), 100);
      } else if (r.newReviewsAdded === 0 && mode === 'update') {
        setTimeout(() => alert('✓ Sudah up-to-date — tidak ada review baru sejak terakhir.'), 100);
      }
    } catch (e) {
      setError(e.message || 'Gagal fetch review outlet');
    } finally {
      setReviewsLoading(false);
    }
  };

  return (
    <>
      <div className="card" style={{ padding: 22, marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <span style={{ fontSize: 16 }}>💬</span>
          <h2 style={{ margin: 0, fontSize: 17, fontWeight: 600 }}>
            Komentar Toko — drill review per outlet
          </h2>
          <span style={{ fontSize: 9.5, padding: '2px 7px', borderRadius: 4, background: 'oklch(0.96 0.05 75)', color: 'oklch(0.55 0.16 75)', fontWeight: 700, marginLeft: 4 }}>TRIAL</span>
        </div>
        <p style={{ margin: '0 0 14px', fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55 }}>
          Cari brand → list semua outlet → klik 1 outlet → lihat <b>semua komentar</b> outlet itu, filter bintang 1 untuk identifikasi keluhan spesifik. Cocok kalau kamu sudah tahu outlet mana yang mau di-evaluasi.
          <br/><b>Cost</b>: 1-3 SerpAPI (cari outlet) + 2 SerpAPI per outlet di-buka.
        </p>

        <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr auto', gap: 10, alignItems: 'flex-end' }}>
          <div>
            <label className="label">Nama Brand</label>
            <input
              className="input"
              value={brand}
              onChange={(e) => setBrand(e.target.value)}
              placeholder="contoh: Geprekin Aja"
              onKeyDown={(e) => e.key === 'Enter' && findPlaces()}
            />
          </div>
          <div>
            <label className="label">Wilayah</label>
            <select className="select" value={regionKey} onChange={(e) => setRegionKey(e.target.value)}>
              {regions.length === 0 ? (
                <option value="indonesia">Indonesia</option>
              ) : regions.map((r) => (
                <option key={r.key} value={r.key}>{r.name}</option>
              ))}
            </select>
          </div>
          <button
            className="btn btn-primary"
            onClick={findPlaces}
            disabled={!brand.trim() || searchingPlaces}
            style={{ padding: '10px 18px' }}
          >
            {searchingPlaces ? 'Mencari…' : '🔎 Cari Outlet'}
          </button>
        </div>
      </div>

      {error && (
        <div style={{ padding: 12, borderRadius: 10, background: 'var(--danger-soft)', color: 'var(--danger)', fontSize: 13, marginBottom: 12 }}>
          {error}
        </div>
      )}

      {/* Outlet list (Step 2) */}
      {places.length > 0 && !activePlace && (
        <div className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 14 }}>
          <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
              {places.length} Outlet — klik untuk lihat komentar
            </span>
            {placesCached ? (
              <span style={{
                fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700,
                background: 'oklch(0.96 0.05 145)', color: 'oklch(0.42 0.14 145)',
              }}>
                💾 Cache · {ageStrAudit(placesAgeMs)}
              </span>
            ) : (
              <span style={{
                fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700,
                background: 'var(--primary-50)', color: 'var(--primary)',
              }}>
                ✨ Fresh
              </span>
            )}
            <button
              onClick={() => {
                if (!confirm('Re-scan outlet dari Google Maps? ~3 SerpAPI searches.')) return;
                findPlaces(true);
              }}
              disabled={searchingPlaces}
              className="btn btn-ghost"
              style={{ marginLeft: 'auto', padding: '4px 10px', fontSize: 11.5 }}
              title="Re-scan outlet (~3 SerpAPI). Outlet bisa berubah kalau brand buka/tutup cabang."
            >↻ Re-scan Outlet</button>
          </div>
          {[...places].sort((a, b) => (a.rating ?? 5) - (b.rating ?? 5)).map((p) => (
            <button
              key={p.dataId}
              onClick={() => openOutlet(p)}
              style={{
                width: '100%', padding: '12px 16px', textAlign: 'left',
                background: 'var(--bg)', border: 'none',
                borderTop: '1px solid var(--line-2)', cursor: 'pointer',
                display: 'grid', gridTemplateColumns: '1fr auto auto auto', gap: 12, alignItems: 'center',
                transition: 'background 120ms',
              }}
              onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-2)'}
              onMouseLeave={(e) => e.currentTarget.style.background = 'var(--bg)'}
            >
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{p.name}</div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{p.address}</div>
              </div>
              <div className="mono" style={{ fontSize: 14, fontWeight: 700, color: ratingColor(p.rating) }}>
                {p.rating?.toFixed(1) ?? '—'} ⭐
              </div>
              <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>
                {(p.reviewsCount || 0).toLocaleString('id-ID')} review
              </div>
              <span style={{ fontSize: 12, color: 'var(--primary)', fontWeight: 600 }}>Lihat →</span>
            </button>
          ))}
        </div>
      )}

      {/* Drill view (Step 3) */}
      {activePlace && (
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div style={{ padding: '14px 16px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <button
              onClick={() => { setActivePlace(null); setReviewsData(null); }}
              className="btn btn-ghost"
              style={{ padding: '5px 10px', fontSize: 12 }}
            >← Kembali</button>
            <div style={{ flex: 1, minWidth: 200 }}>
              <div style={{ fontSize: 14, fontWeight: 700 }}>{activePlace.name}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{activePlace.address}</div>
            </div>
            <div className="mono" style={{ fontSize: 16, fontWeight: 700, color: ratingColor(activePlace.rating) }}>
              {activePlace.rating?.toFixed(1) ?? '—'} ⭐
            </div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>
              {(activePlace.reviewsCount || 0).toLocaleString('id-ID')} review
            </div>
            <div style={{ display: 'flex', gap: 4 }}>
              <button
                onClick={() => openOutlet(activePlace, 'update')}
                disabled={reviewsLoading}
                className="btn btn-soft"
                style={{ padding: '5px 10px', fontSize: 11.5 }}
                title="Fetch hanya review baru sejak terakhir update — 1 SerpAPI call"
              >🔄 Update Terbaru</button>
              <button
                onClick={() => {
                  if (!confirm('Re-scan penuh akan re-fetch SEMUA review (3 SerpAPI calls). Lanjutkan?')) return;
                  openOutlet(activePlace, 'full');
                }}
                disabled={reviewsLoading}
                className="btn btn-ghost"
                style={{ padding: '5px 10px', fontSize: 11.5 }}
                title="Re-scan lengkap 3 SerpAPI — refresh semua review (timpa data lama)"
              >↻ Re-scan</button>
            </div>
          </div>

          {reviewsLoading && (
            <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
              Memuat review outlet…
            </div>
          )}

          {!reviewsLoading && reviewsData && (
            <DrilledReviewsView
              reviews={reviewsData.reviews || []}
              cachedAt={reviewsCachedAt}
            />
          )}
        </div>
      )}
    </>
  );
}

// Bagian list review dalam drill view — filter bintang + search keyword
function DrilledReviewsView({ reviews, cachedAt }) {
  const [mode, setMode] = React.useState(1); // default ⭐1 saja
  const [search, setSearch] = React.useState('');

  const counts = React.useMemo(() => {
    const c = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, total: reviews.length };
    for (const rv of reviews) {
      const r = Math.max(1, Math.min(5, Math.round(rv.rating || 0)));
      c[r] = (c[r] || 0) + 1;
    }
    c.lo12 = c[1] + c[2];
    return c;
  }, [reviews]);

  React.useEffect(() => {
    if (mode === 1 && counts[1] === 0 && counts.lo12 > 0) setMode(2);
  }, [counts, mode]);

  const filtered = React.useMemo(() => {
    let list = reviews;
    if (mode === 1) list = list.filter((rv) => rv.rating === 1);
    else if (mode === 2) list = list.filter((rv) => rv.rating <= 2);
    if (search.trim()) {
      const q = search.toLowerCase();
      list = list.filter((rv) => rv.text.toLowerCase().includes(q));
    }
    list = [...list].sort((a, b) => {
      const da = a.isoDate || ''; const db = b.isoDate || '';
      if (da !== db) return db.localeCompare(da);
      return (a.rating || 0) - (b.rating || 0);
    });
    return list;
  }, [reviews, mode, search]);

  const ageStr = (ms) => {
    if (ms == null || ms < 60_000) return 'baru saja';
    const min = Math.floor(ms / 60_000);
    if (min < 60) return `${min} mnt lalu`;
    const hr = Math.floor(min / 60);
    if (hr < 24) return `${hr} jam lalu`;
    return `${Math.floor(hr / 24)} hari lalu`;
  };

  return (
    <>
      {/* Diagnostic + filter bar */}
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', background: 'var(--bg-2)' }}>
        <div style={{ flex: 1, minWidth: 200, fontSize: 11.5, color: 'var(--ink-3)' }}>
          {counts.total} review ·
          <b style={{ color: 'oklch(0.42 0.14 25)', marginLeft: 4 }}>{counts[1]}⭐1</b>&nbsp;·&nbsp;
          <b style={{ color: 'oklch(0.55 0.16 25)' }}>{counts[2]}⭐2</b>&nbsp;·&nbsp;
          <b style={{ color: 'oklch(0.55 0.16 75)' }}>{counts[3]}⭐3</b>&nbsp;·&nbsp;
          <b style={{ color: 'oklch(0.45 0.13 145)' }}>{counts[4]}⭐4</b>&nbsp;·&nbsp;
          <b style={{ color: 'oklch(0.42 0.14 155)' }}>{counts[5]}⭐5</b>
          {cachedAt != null && cachedAt > 0 && (
            <span style={{ marginLeft: 10, color: 'var(--ink-4)' }}>· cache {ageStr(cachedAt)}</span>
          )}
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          {[
            { id: 1, label: '⭐1', count: counts[1] },
            { id: 2, label: '⭐1-2', count: counts.lo12 },
            { id: 'all', label: 'Semua', count: counts.total },
          ].map((opt) => (
            <button
              key={opt.id}
              onClick={() => setMode(opt.id)}
              disabled={opt.count === 0}
              style={{
                padding: '5px 10px', fontSize: 11.5, fontWeight: 700,
                borderRadius: 6, border: 'none',
                cursor: opt.count === 0 ? 'not-allowed' : 'pointer',
                background: mode === opt.id ? 'oklch(0.55 0.16 25)' : 'var(--bg)',
                color: mode === opt.id ? 'white' : (opt.count === 0 ? 'var(--ink-4)' : 'var(--ink-2)'),
                opacity: opt.count === 0 ? 0.5 : 1,
                display: 'inline-flex', alignItems: 'center', gap: 4,
              }}
            >
              {opt.label}
              <span className="mono" style={{ fontSize: 9.5, opacity: 0.85 }}>{opt.count}</span>
            </button>
          ))}
        </div>
      </div>

      <div style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)' }}>
        <input
          className="input"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder='Cari kata kunci di komentar — "lambat", "antri", "ayam", dst'
          style={{ fontSize: 12.5 }}
        />
      </div>

      <div style={{ maxHeight: 600, overflow: 'auto' }} className="scroll">
        {filtered.length === 0 && (
          <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12.5, lineHeight: 1.6 }}>
            {search.trim()
              ? <>Tidak ada komentar yang mengandung "<b>{search}</b>".</>
              : mode === 1 && counts[1] === 0
              ? <>🎉 Tidak ada review <b>⭐1</b> di outlet ini.<br/><span style={{ fontSize: 11, color: 'var(--ink-4)' }}>Switch ke ⭐1-2 atau Semua untuk lihat review lain.</span></>
              : <>Tidak ada review pada filter ini.</>
            }
          </div>
        )}
        {filtered.map((rv, i) => (
          <div key={i} style={{
            padding: '12px 16px',
            borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
          }}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 4 }}>
              <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: ratingColor(rv.rating) }}>
                {'⭐'.repeat(rv.rating)} {rv.rating}/5
              </span>
              <span style={{ fontSize: 10.5, color: 'var(--ink-4)', marginLeft: 'auto' }}>
                {rv.date} · {rv.user || 'Anonim'}
              </span>
            </div>
            <div style={{ fontSize: 13, color: 'var(--ink)', lineHeight: 1.55 }}>
              {search.trim() ? highlightMatch(rv.text, search.toLowerCase()) : rv.text}
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

// ─── Tab: YouTube Insight ───────────────────────────────────────────

// ─── BrandAudit wrapper — 3 internal tabs ───────────────────────────
function BrandAuditView() {
  const [tab, setTab] = React.useState('audit'); // 'audit' | 'dash' | 'outlet'
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <TopBar
        title="Brand Audit"
        subtitle={
          tab === 'audit'  ? 'Brand Audit — Google Maps reviews via SerpAPI'
          : tab === 'dash' ? 'Dashboard — overview brand audit + AI insight'
          :                  'Komentar Toko — drill review per outlet'
        }
      />
      <div className="scroll" style={{ flex: 1, overflow: 'auto', padding: 24, background: 'var(--bg)' }}>
        <div style={{ maxWidth: 1100, margin: '0 auto' }}>
          <div className="tabs" style={{ marginBottom: 18 }}>
            <button className={`tab ${tab === 'audit' ? 'active' : ''}`} onClick={() => setTab('audit')}>
              🏪 Brand Audit <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 4, background: 'oklch(0.96 0.05 75)', color: 'oklch(0.55 0.16 75)', marginLeft: 4, fontWeight: 700 }}>TRIAL</span>
            </button>
            <button className={`tab ${tab === 'dash' ? 'active' : ''}`} onClick={() => setTab('dash')}>
              📊 Dashboard
            </button>
            <button className={`tab ${tab === 'outlet' ? 'active' : ''}`} onClick={() => setTab('outlet')}>
              💬 Komentar Toko <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 4, background: 'oklch(0.96 0.05 75)', color: 'oklch(0.55 0.16 75)', marginLeft: 4, fontWeight: 700 }}>TRIAL</span>
            </button>
          </div>

          {tab === 'audit' && <SuperAdminBrandAudit />}
          {tab === 'dash' && <SuperAdminAuditDashboard />}
          {tab === 'outlet' && <SuperAdminOutletReviews />}
        </div>
      </div>
    </div>
  );
}

window.BrandAuditView = BrandAuditView;
