// Social Insight — TikTok + Instagram analytics (extracted from Super Admin).
// Accessible to all logged-in users via sidebar "Social Insight".
// Apify settings (token, actor IDs) tetap di Super Admin → Integrasi.

// ─── Tab: TikTok Insight (via Apify) ─────────────────────────────────
function SuperAdminTikTokInsight() {
  const useState_tt = React.useState;
  const useEffect_tt = React.useEffect;
  const [mode, setMode] = useState_tt('search'); // 'search' | 'dashboard' | 'compare'
  const [searchType, setSearchType] = useState_tt('keyword'); // 'keyword' | 'profile'
  const [dateRange, setDateRange] = useState_tt('all'); // 'all' | '7d' | '30d' | '90d'
  const [maxItems, setMaxItems] = useState_tt(25); // 25 | 50 | 100 | 200
  const [compareBrands, setCompareBrands] = useState_tt([]); // selected brand keys
  const [compareData, setCompareData] = useState_tt(null);
  const [compareLoading, setCompareLoading] = useState_tt(false);
  const [brand, setBrand] = useState_tt('');
  const [videos, setVideos] = useState_tt([]);
  const [searching, setSearching] = useState_tt(false);
  const [searchCached, setSearchCached] = useState_tt(false);
  const [searchAge, setSearchAge] = useState_tt(0);
  const [history, setHistory] = useState_tt([]);
  const [activeVideo, setActiveVideo] = useState_tt(null);
  const [error, setError] = useState_tt(null);
  const [actorId, setActorId] = useState_tt(null);
  const [dashboard, setDashboard] = useState_tt(null);
  const [dashLoading, setDashLoading] = useState_tt(false);
  const [reportData, setReportData] = useState_tt(null);
  const [reportLoading, setReportLoading] = useState_tt(false);

  useEffect_tt(() => {
    window.api.listTiktokSearches(searchType).then((r) => setHistory(r.searches || [])).catch(() => {});
  }, [searchType]);

  // Reset state saat ganti searchType — supaya gak campur data keyword vs profile
  useEffect_tt(() => {
    setBrand('');
    setVideos([]);
    setActiveVideo(null);
    setDashboard(null);
    setCompareBrands([]);
    setCompareData(null);
    setError(null);
  }, [searchType]);

  const loadDashboard = async (b, range) => {
    if (!b) return;
    setDashLoading(true); setError(null);
    try {
      const r = await window.api.tiktokDashboard(b, range || dateRange, searchType);
      setDashboard(r);
    } catch (e) {
      setError(e.message || 'Gagal load dashboard');
      setDashboard(null);
    } finally {
      setDashLoading(false);
    }
  };

  const runCompare = async (range) => {
    if (compareBrands.length < 2) return;
    setCompareLoading(true); setError(null);
    try {
      const r = await window.api.tiktokCompare(compareBrands, range || dateRange, searchType);
      setCompareData(r);
    } catch (e) {
      setError(e.message || 'Gagal compare');
      setCompareData(null);
    } finally {
      setCompareLoading(false);
    }
  };

  // Re-load dashboard / compare / search ketika dateRange berubah
  React.useEffect(() => {
    if (mode === 'dashboard' && brand.trim() && dashboard) {
      loadDashboard(brand.trim(), dateRange);
    } else if (mode === 'compare' && compareBrands.length >= 2 && compareData) {
      runCompare(dateRange);
    } else if (mode === 'search' && brand.trim() && videos.length > 0) {
      // Re-filter from cache (no Apify call) — backend reads from DB
      doSearch(false, dateRange);
    }
  }, [dateRange]);

  const toggleCompareBrand = (brandKey) => {
    setCompareBrands((prev) => {
      if (prev.includes(brandKey)) return prev.filter((b) => b !== brandKey);
      if (prev.length >= 5) return prev; // max 5
      return [...prev, brandKey];
    });
    setCompareData(null);
  };

  const [searchTotalCount, setSearchTotalCount] = useState_tt(0);
  const [searchFilteredCount, setSearchFilteredCount] = useState_tt(0);

  const doSearch = async (force, range) => {
    if (!brand.trim()) return;
    if (force) {
      const estCost = maxItems <= 25 ? '$0.01-0.10' : maxItems <= 50 ? '$0.02-0.20' : maxItems <= 100 ? '$0.05-0.40' : '$0.10-0.80';
      if (!confirm(`Re-scan TikTok ${maxItems} video?\n\nEstimasi cost Apify: ${estCost}\n(tergantung actor pricing)`)) return;
    }
    setSearching(true); setError(null); setActiveVideo(null);
    try {
      const r = await window.api.tiktokSearch({ brand: brand.trim(), force: !!force, dateRange: range || dateRange, maxItems, searchType });
      setVideos(r.videos || []);
      setSearchCached(!!r.cached);
      setSearchAge(r.ageMs || 0);
      setActorId(r.actor || null);
      setSearchTotalCount(r.totalCount || (r.videos || []).length);
      setSearchFilteredCount(r.filteredCount ?? (r.videos || []).length);
      // Warning kalau actor return jauh lebih sedikit dari yang diminta
      if (!r.cached && r.requestedMaxItems && r.actualScraped != null && r.actualScraped < r.requestedMaxItems * 0.6) {
        const sourceHint = searchType === 'profile' ? 'profile tidak punya cukup video' : 'brand tidak punya cukup konten di TikTok';
        setError(`⚠ Apify return ${r.actualScraped} video dari ${r.requestedMaxItems} yang diminta. Kemungkinan: (1) actor cap di plan kamu, (2) ${sourceHint}, (3) actor input field beda — coba ganti actor di Integrasi.`);
      }
      window.api.listTiktokSearches(searchType).then((rr) => setHistory(rr.searches || []));
    } catch (e) {
      setError(e.message || 'Gagal cari video TikTok');
    } finally {
      setSearching(false);
    }
  };

  // Auto-load dashboard saat switch mode. Kalau brand belum di-set tapi ada
  // history, auto-pick yang paling baru.
  React.useEffect(() => {
    if (mode !== 'dashboard') return;
    const target = brand.trim() || (history[0]?.brandKey ?? '');
    if (target) {
      if (!brand.trim()) setBrand(target);
      loadDashboard(target);
    }
  }, [mode, history.length]);

  const fmtCount = (n) => {
    if (n == null) return '—';
    if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
    if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
    return String(n);
  };

  const deleteSearch = async (brandKey) => {
    if (!confirm(`Hapus search untuk "${brandKey}"?\n\nCached data akan dihapus permanen (perlu re-scrape Apify kalau mau lihat lagi).`)) return;
    try {
      await window.api.deleteTiktokSearch(brandKey, searchType);
      const rr = await window.api.listTiktokSearches(searchType);
      setHistory(rr.searches || []);
      // Kalau brand yang dihapus lagi di-view, clear state-nya
      if (brand.trim().toLowerCase() === brandKey.toLowerCase()) {
        setBrand(''); setVideos([]); setDashboard(null); setActiveVideo(null);
      }
      setCompareBrands((prev) => prev.filter((b) => b.toLowerCase() !== brandKey.toLowerCase()));
    } catch (e) {
      setError(e.message || 'Gagal hapus search');
    }
  };

  const exportReport = async () => {
    if (!dashboard || !brand.trim()) return;
    setReportLoading(true); setError(null);
    try {
      const stats = dashboard.stats || {};
      const payload = {
        brand: brand.trim(),
        dateRange,
        searchType,
        stats: {
          itemCount: stats.videoCount || 0,
          totalReach: stats.totalViews || 0,
          totalLikes: stats.totalLikes || 0,
          totalComments: stats.totalComments || 0,
          totalShares: stats.totalShares || 0,
          avgEngagementRate: stats.avgEngagementRate || 0,
        },
        topCreators: (dashboard.topCreators || []).slice(0, 8).map((c) => ({
          author: c.author, fans: c.fans, posts: c.posts,
          totalReach: c.totalViews || 0, totalLikes: c.totalLikes || 0,
          engagementRate: c.avgEngagementRate || 0,
        })),
        topHashtags: (dashboard.topHashtags || []).slice(0, 10).map((h) => ({
          tag: h.tag, freq: h.freq, totalReach: h.totalViews || 0,
        })),
        audienceTiers: dashboard.audienceTiers || { nano: 0, micro: 0, mid: 0, macro: 0, mega: 0, total: 0 },
        topContent: (dashboard.topVideosByViews || []).slice(0, 6).map((v) => ({
          caption: v.caption || '', author: v.author || '',
          views: v.views || 0, likes: v.likes || 0,
          commentCount: v.commentCount || 0, url: v.url || '',
        })),
        weeklyTimeline: (dashboard.weeklyTimeline || []).slice(-8),
        comments: dashboard.commentStats && dashboard.commentStats.totalScraped > 0 && dashboard.commentAi ? {
          totalScraped: dashboard.commentStats.totalScraped,
          coverage: dashboard.commentStats.coverage || 0,
          overallSentiment: dashboard.commentAi.overallSentiment || null,
          complaints: dashboard.commentAi.complaints || [],
          praises: dashboard.commentAi.praises || [],
        } : null,
      };
      const r = await window.api.tiktokReportNarrative(payload);
      setReportData({
        platform: 'tiktok',
        brand: brand.trim(),
        dateRange,
        searchType,
        dashboard,
        narrative: r.narrative,
        generatedAt: new Date(),
      });
    } catch (e) {
      setError(e.message || 'Gagal generate laporan');
    } finally {
      setReportLoading(false);
    }
  };

  return (
    <>
      <div className="card" style={{ padding: 22, marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
          <span style={{ fontSize: 16 }}>🎵</span>
          <h2 style={{ margin: 0, fontSize: 17, fontWeight: 600 }}>
            TikTok Insight
          </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 style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
            {/* Search type toggle: Keyword / Profile */}
            <div style={{ display: 'inline-flex', borderRadius: 6, overflow: 'hidden', border: '1px solid var(--line)' }}>
              <button onClick={() => setSearchType('keyword')} style={{
                padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: searchType === 'keyword' ? 'oklch(0.55 0.18 320)' : 'var(--bg)',
                color: searchType === 'keyword' ? 'white' : 'var(--ink-2)',
              }}>🔤 Keyword</button>
              <button onClick={() => setSearchType('profile')} style={{
                padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: searchType === 'profile' ? 'oklch(0.55 0.18 320)' : 'var(--bg)',
                color: searchType === 'profile' ? 'white' : 'var(--ink-2)',
                borderLeft: '1px solid var(--line)',
              }}>👤 Profile</button>
            </div>
            {/* Date range filter — Dashboard, Compare, Search (cache filter) */}
            {(mode === 'dashboard' || mode === 'compare' || mode === 'search') && (
              <div style={{ display: 'inline-flex', borderRadius: 6, overflow: 'hidden', border: '1px solid var(--line)' }}>
                {[
                  { id: 'all', label: 'Semua' },
                  { id: '90d', label: '90 hari' },
                  { id: '30d', label: '30 hari' },
                  { id: '7d', label: '7 hari' },
                ].map((opt) => (
                  <button key={opt.id} onClick={() => setDateRange(opt.id)} style={{
                    padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                    background: dateRange === opt.id ? 'var(--primary)' : 'var(--bg)',
                    color: dateRange === opt.id ? 'white' : 'var(--ink-2)',
                    borderLeft: opt.id !== 'all' ? '1px solid var(--line)' : 'none',
                  }}>{opt.label}</button>
                ))}
              </div>
            )}
            <div style={{ display: 'inline-flex', borderRadius: 8, overflow: 'hidden', border: '1px solid var(--line)', background: 'var(--bg-2)' }}>
              <button onClick={() => setMode('search')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'search' ? 'var(--bg)' : 'transparent',
                color: mode === 'search' ? 'var(--ink)' : 'var(--ink-3)',
                borderRight: '1px solid var(--line)',
              }}>🔎 Search</button>
              <button onClick={() => setMode('dashboard')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'dashboard' ? 'var(--bg)' : 'transparent',
                color: mode === 'dashboard' ? 'var(--ink)' : 'var(--ink-3)',
                borderRight: '1px solid var(--line)',
              }}>📊 Dashboard</button>
              <button onClick={() => setMode('compare')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'compare' ? 'var(--bg)' : 'transparent',
                color: mode === 'compare' ? 'var(--ink)' : 'var(--ink-3)',
              }}>⚖️ Compare</button>
            </div>
          </div>
        </div>
        <p style={{ margin: '0 0 14px', fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55 }}>
          {mode === 'search'
            ? (searchType === 'profile'
                ? <>Scrape video TikTok dari <b>profile</b> (kompetitor / KOL). Input multi username pakai koma — bisa sampai 200 video per profile. <b>Cost</b>: 1 Apify run per search.</>
                : <>Scrape video TikTok yang menyebut brand → list video + creator + stats. <b>Cost</b>: 1 Apify run per search.</>
              )
            : mode === 'dashboard'
            ? <>Analytics dashboard dari data {searchType === 'profile' ? 'Profile' : 'Keyword'} — KPI, top performer, KOL identification, hashtag trending, AI strategic insight. <b>0 Apify run</b> (read DB only).</>
            : <>Bandingkan TOM (Top of Mind) antar brand di TikTok ({searchType === 'profile' ? 'Profile' : 'Keyword'}) — Share of Voice, Reach, Engagement. Pilih 2-5 brand dari history, AI kasih competitive insight. <b>0 Apify run</b>.</>
          }
        </p>

        {mode !== 'compare' ? (
          <div style={{ display: 'grid', gridTemplateColumns: mode === 'search' ? '1fr auto auto' : '1fr auto', gap: 10, alignItems: 'flex-end' }}>
            <div>
              <label className="label">
                {searchType === 'profile' ? 'Username Profile' : 'Brand / kata kunci'}
                {mode === 'search' && searchType === 'profile' && <span style={{ fontWeight: 400, color: 'var(--ink-3)' }}> · multi pakai koma</span>}
              </label>
              <input
                className="input"
                value={brand}
                onChange={(e) => setBrand(e.target.value)}
                placeholder={searchType === 'profile'
                  ? (mode === 'search' ? "@geprekinaja, geprekin.id, @karirgeprekinaja" : "@geprekinaja")
                  : "contoh: Geprekin Aja, kopi kenanga viral"
                }
                onKeyDown={(e) => e.key === 'Enter' && (mode === 'search' ? doSearch(false) : loadDashboard(brand.trim()))}
              />
              {mode === 'search' && searchType === 'profile' && (
                <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 4 }}>
                  Input username profile TikTok (boleh pakai @). Akan scrape video terbaru dari masing-masing akun.
                </div>
              )}
            </div>
            {mode === 'search' && (
              <div>
                <label className="label">Jumlah Video</label>
                <select
                  className="select"
                  value={maxItems}
                  onChange={(e) => setMaxItems(Number(e.target.value))}
                  style={{ fontSize: 12.5, minWidth: 110 }}
                  title={searchType === 'profile'
                    ? "Limit per profile (kalau input multi username, ini per akun)"
                    : "Lebih banyak = lebih lama scrape + lebih mahal Apify"}
                >
                  <option value={25}>25 video</option>
                  <option value={50}>50 video</option>
                  <option value={100}>100 video</option>
                  <option value={200}>200 video</option>
                </select>
              </div>
            )}
            <button
              className="btn btn-primary"
              onClick={() => mode === 'search' ? doSearch(false) : loadDashboard(brand.trim())}
              disabled={!brand.trim() || searching || dashLoading}
              style={{ padding: '10px 18px' }}
            >
              {mode === 'search'
                ? (searching ? `Scraping ${maxItems}…` : `🔎 Cari ${maxItems} Video`)
                : (dashLoading ? 'Loading…' : '📊 Buka Dashboard')
              }
            </button>
          </div>
        ) : (
          <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>
            👇 Pilih brand dari grid di bawah (min 2, max 5), lalu klik <b>Bandingkan</b>.
          </div>
        )}
      </div>

      {error && (
        <div style={{ padding: 12, borderRadius: 10, background: 'var(--danger-soft)', color: 'var(--danger)', fontSize: 13, marginBottom: 12, lineHeight: 1.55 }}>
          <b>Gagal:</b> {error}
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 6 }}>
            Pastikan: (1) Apify token tersimpan & valid di tab Integrasi, (2) Apify Actor ID terisi (default: <span className="mono">clockworks/tiktok-scraper</span>) atau pilih actor lain dari <a href="https://apify.com/store?search=tiktok" target="_blank" rel="noreferrer" style={{ color: 'var(--primary)' }}>Apify Store</a>, (3) akun Apify punya credit cukup.
          </div>
        </div>
      )}

      {/* Dashboard mode: history list (kalau ada data tersimpan) */}
      {mode === 'dashboard' && history.length > 0 && (
        <div className="card" style={{ padding: 14, marginBottom: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 6 }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
              💾 Brand Tersimpan ({history.length}) — klik untuk buka dashboard
            </span>
            <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>0 Apify call · read DB only</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 8 }}>
            {history.map((h, i) => {
              const isActive = brand.trim().toLowerCase() === h.brandKey.toLowerCase();
              return (
                <div
                  key={i}
                  onClick={() => { setBrand(h.brandKey); loadDashboard(h.brandKey); }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10,
                    padding: '10px 12px',
                    background: isActive ? 'var(--primary-50)' : 'var(--bg)',
                    border: `1px solid ${isActive ? 'var(--primary)' : 'var(--line)'}`,
                    borderRadius: 8, cursor: 'pointer', textAlign: 'left',
                    transition: 'all 120ms',
                  }}
                  onMouseEnter={(e) => { if (!isActive) { e.currentTarget.style.borderColor = 'var(--primary)'; e.currentTarget.style.background = 'var(--primary-50)'; } }}
                  onMouseLeave={(e) => { if (!isActive) { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.background = 'var(--bg)'; } }}
                >
                  <span style={{ fontSize: 18 }}>🎵</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: isActive ? 'var(--primary)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {h.brandKey}
                    </div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>
                      {ageStrAudit(Date.now() - new Date(h.fetchedAt).getTime())}
                    </div>
                  </div>
                  {isActive && <span style={{ fontSize: 14, color: 'var(--primary)' }}>●</span>}
                  <button
                    onClick={(e) => { e.stopPropagation(); deleteSearch(h.brandKey); }}
                    title="Hapus search"
                    style={{
                      width: 22, height: 22, padding: 0, lineHeight: 1,
                      background: 'transparent', border: '1px solid var(--line)', borderRadius: 6,
                      color: 'var(--ink-3)', fontSize: 14, cursor: 'pointer',
                    }}
                    onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--danger)'; e.currentTarget.style.color = 'var(--danger)'; }}
                    onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                  >×</button>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {mode === 'dashboard' && dashLoading && (
        <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>Memuat analytics dashboard…</div>
      )}
      {mode === 'dashboard' && dashboard && (
        <>
          <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }}>
            <button
              onClick={exportReport}
              disabled={reportLoading}
              className="btn"
              style={{
                padding: '8px 16px', fontSize: 12.5, fontWeight: 600,
                background: 'oklch(0.32 0.05 250)', color: 'white', border: 'none',
                borderRadius: 8, cursor: reportLoading ? 'wait' : 'pointer',
                boxShadow: '0 1px 2px rgba(0,0,0,0.1)',
              }}
              title="Generate laporan profesional untuk BOD/investor (2 AI call, ~10-20 detik)"
            >
              {reportLoading ? '📄 Menyusun laporan AI…' : '📄 Export Laporan PDF'}
            </button>
          </div>
          <TikTokDashboardView data={dashboard} />
        </>
      )}
      {mode === 'dashboard' && !dashboard && !dashLoading && history.length === 0 && (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10 }}>
          Belum ada data brand tersimpan. Switch ke <b>🔎 Search</b> untuk scrape brand pertama.
        </div>
      )}

      {reportData && window.SocialReportView && (
        <window.SocialReportView data={reportData} onClose={() => setReportData(null)} />
      )}

      {/* Compare mode: brand selector grid + run button + result view */}
      {mode === 'compare' && (
        <>
          {history.length < 2 ? (
            <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10 }}>
              Butuh minimal <b>2 brand</b> tersimpan untuk bandingkan. Switch ke <b>🔎 Search</b> dan scrape minimal 2 brand kompetitor.
            </div>
          ) : (
            <>
              <div className="card" style={{ padding: 14, marginBottom: 14 }}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 6 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                    ⚖️ Pilih Brand untuk Dibandingkan ({compareBrands.length}/5)
                  </span>
                  <button
                    className="btn btn-primary"
                    onClick={() => runCompare()}
                    disabled={compareBrands.length < 2 || compareLoading}
                    style={{ padding: '6px 14px', fontSize: 12 }}
                  >
                    {compareLoading ? 'Computing…' : `📊 Bandingkan ${compareBrands.length} Brand`}
                  </button>
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 8 }}>
                  {history.map((h, i) => {
                    const checked = compareBrands.includes(h.brandKey);
                    const order = compareBrands.indexOf(h.brandKey) + 1;
                    return (
                      <button
                        key={i}
                        onClick={() => toggleCompareBrand(h.brandKey)}
                        disabled={!checked && compareBrands.length >= 5}
                        style={{
                          display: 'grid', gridTemplateColumns: '20px 1fr auto', gap: 8, alignItems: 'center',
                          padding: '10px 12px',
                          background: checked ? 'var(--primary-50)' : 'var(--bg)',
                          border: `1px solid ${checked ? 'var(--primary)' : 'var(--line)'}`,
                          borderRadius: 8, cursor: 'pointer', textAlign: 'left',
                          opacity: !checked && compareBrands.length >= 5 ? 0.4 : 1,
                        }}
                      >
                        <input type="checkbox" checked={checked} readOnly style={{ pointerEvents: 'none' }} />
                        <span style={{ fontSize: 13, fontWeight: 700, color: checked ? 'var(--primary)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                          {h.brandKey}
                        </span>
                        {checked && (
                          <span className="mono" style={{ fontSize: 10.5, padding: '2px 6px', borderRadius: 999, background: 'var(--primary)', color: 'white', fontWeight: 700 }}>
                            #{order}
                          </span>
                        )}
                      </button>
                    );
                  })}
                </div>
                {compareBrands.length === 1 && (
                  <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 8 }}>
                    Pilih min 1 brand lagi (kompetitor) untuk bandingkan TOM-nya.
                  </div>
                )}
              </div>

              {compareLoading && (
                <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>Menghitung perbandingan…</div>
              )}
              {compareData && <TikTokCompareView data={compareData} />}
            </>
          )}
        </>
      )}

      {mode === 'search' && history.length > 0 && videos.length === 0 && !activeVideo && (
        <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 }}>
            Search terakhir
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {history.slice(0, 8).map((h, i) => (
              <div
                key={i}
                onClick={() => { setBrand(h.brandKey); setTimeout(() => doSearch(false), 0); }}
                style={{
                  display: 'grid', gridTemplateColumns: '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 }}>{h.brandKey}</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{new Date(h.fetchedAt).toLocaleDateString('id-ID')}</span>
                <button
                  onClick={(e) => { e.stopPropagation(); deleteSearch(h.brandKey); }}
                  title="Hapus search"
                  style={{
                    width: 22, height: 22, padding: 0, lineHeight: 1,
                    background: 'transparent', border: '1px solid var(--line)', borderRadius: 6,
                    color: 'var(--ink-3)', fontSize: 14, cursor: 'pointer',
                  }}
                  onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--danger)'; e.currentTarget.style.color = 'var(--danger)'; }}
                  onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                >×</button>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Video list */}
      {/* Empty state setelah filter menghasilkan 0 video */}
      {mode === 'search' && videos.length === 0 && searchTotalCount > 0 && dateRange !== 'all' && !searching && (
        <div className="card" style={{ padding: 24, textAlign: 'center' }}>
          <div style={{ fontSize: 32, marginBottom: 8 }}>📅</div>
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-2)', marginBottom: 4 }}>
            Tidak ada video di filter "{dateRange === '7d' ? '7 hari' : dateRange === '30d' ? '30 hari' : '90 hari'} terakhir"
          </div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginBottom: 12 }}>
            Total {searchTotalCount} video tersimpan, tapi tidak ada yang cocok dengan rentang tanggal.
          </div>
          <button onClick={() => setDateRange('all')} className="btn btn-ghost" style={{ fontSize: 11.5 }}>
            Lihat Semua Waktu
          </button>
        </div>
      )}

      {mode === 'search' && videos.length > 0 && !activeVideo && (
        <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' }}>
              {videos.length} Video
              {dateRange !== 'all' && searchTotalCount > videos.length && (
                <span style={{ fontWeight: 500, opacity: 0.7, marginLeft: 4, textTransform: 'none', letterSpacing: 0 }}>
                  (dari {searchTotalCount})
                </span>
              )}
            </span>
            {dateRange !== 'all' && (
              <span style={{
                fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700,
                background: 'var(--primary-50)', color: 'var(--primary)',
                textTransform: 'uppercase', letterSpacing: '0.04em',
              }}>
                📅 {dateRange === '7d' ? '7 hari' : dateRange === '30d' ? '30 hari' : '90 hari'} terakhir
              </span>
            )}
            {searchCached ? (
              <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)' }}>
                💾 Tersimpan · {ageStrAudit(searchAge)}
              </span>
            ) : (
              <span style={{ fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700, background: 'var(--primary-50)', color: 'var(--primary)' }}>
                ✨ Fresh{actorId && ` · ${actorId}`}
              </span>
            )}
            <button
              onClick={() => doSearch(true)}
              disabled={searching}
              className="btn btn-ghost"
              style={{ marginLeft: 'auto', padding: '4px 10px', fontSize: 11.5 }}
              title={dateRange !== 'all' ? `Re-scan dengan filter ${dateRange}` : 'Re-scan TikTok (1 Apify run)'}
            >↻ Re-scan</button>
          </div>
          <div>
            {videos.map((v) => (
              <button
                key={v.videoId}
                onClick={() => setActiveVideo(v)}
                style={{
                  width: '100%', padding: '12px 16px', textAlign: 'left',
                  background: 'var(--bg)', border: 'none',
                  borderTop: '1px solid var(--line-2)', cursor: 'pointer',
                  display: 'grid', gridTemplateColumns: '90px 1fr 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)'}
              >
                {v.cover ? (
                  <img src={v.cover} alt="" referrerPolicy="no-referrer" style={{ width: 90, height: 120, objectFit: 'cover', borderRadius: 6, background: 'var(--bg-2)' }} />
                ) : (
                  <div style={{ width: 90, height: 120, background: 'linear-gradient(135deg, oklch(0.92 0.04 290), oklch(0.92 0.04 25))', borderRadius: 6, display: 'grid', placeItems: 'center', color: 'white', fontSize: 28 }}>🎵</div>
                )}
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--primary)' }}>@{v.author}</span>
                    {v.authorVerified && <span style={{ fontSize: 10, color: 'var(--primary)' }}>✓</span>}
                    {v.authorFans != null && (
                      <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>· {fmtCount(v.authorFans)} fans</span>
                    )}
                  </div>
                  <div style={{ fontSize: 12.5, color: 'var(--ink)', marginBottom: 6, lineHeight: 1.4, maxWidth: 460 }}>
                    {v.caption.length > 140 ? v.caption.slice(0, 139) + '…' : v.caption}
                  </div>
                  <div style={{ display: 'flex', gap: 12, fontSize: 11, color: 'var(--ink-3)', flexWrap: 'wrap' }}>
                    <span>👁 {fmtCount(v.views)}</span>
                    <span>❤️ {fmtCount(v.likes)}</span>
                    <span>💬 {fmtCount(v.commentCount)}</span>
                    <span>↗ {fmtCount(v.shareCount)}</span>
                    {v.publishedDate && <span>· {new Date(v.publishedDate).toLocaleDateString('id-ID')}</span>}
                  </div>
                  {v.hashtags && v.hashtags.length > 0 && (
                    <div style={{ display: 'flex', gap: 4, marginTop: 6, flexWrap: 'wrap' }}>
                      {v.hashtags.slice(0, 5).map((h, i) => (
                        <span key={i} style={{ fontSize: 10, padding: '1px 6px', borderRadius: 999, background: 'var(--primary-50)', color: 'var(--primary)', fontWeight: 600 }}>#{h}</span>
                      ))}
                    </div>
                  )}
                </div>
                <span style={{ fontSize: 11.5, color: 'var(--primary)', fontWeight: 600 }}>Detail →</span>
              </button>
            ))}
          </div>
        </div>
      )}

      {/* Drill view (minimal — open in TikTok kalau mau lihat full) */}
      {mode === 'search' && activeVideo && (
        <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={() => setActiveVideo(null)} className="btn btn-ghost" style={{ padding: '5px 10px', fontSize: 12 }}>← Kembali</button>
            <div style={{ flex: 1, minWidth: 200 }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--primary)' }}>@{activeVideo.author}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
                {activeVideo.publishedDate && new Date(activeVideo.publishedDate).toLocaleDateString('id-ID')}
                {activeVideo.duration && ` · ${activeVideo.duration}s`}
                {activeVideo.musicTitle && ` · 🎵 ${activeVideo.musicTitle}`}
              </div>
            </div>
            <a href={activeVideo.url} target="_blank" rel="noreferrer" className="btn btn-primary" style={{ padding: '6px 14px', fontSize: 12, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              🎵 Tonton di TikTok
            </a>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '200px 1fr', gap: 18, padding: 18 }}>
            {activeVideo.cover ? (
              <img src={activeVideo.cover} alt="" referrerPolicy="no-referrer" style={{ width: 200, height: 268, objectFit: 'cover', borderRadius: 10, background: 'var(--bg-2)' }} />
            ) : (
              <div style={{ width: 200, height: 268, background: 'linear-gradient(135deg, oklch(0.92 0.04 290), oklch(0.92 0.04 25))', borderRadius: 10, display: 'grid', placeItems: 'center', color: 'white', fontSize: 60 }}>🎵</div>
            )}
            <div>
              <div style={{ fontSize: 13.5, color: 'var(--ink)', lineHeight: 1.6, marginBottom: 14, whiteSpace: 'pre-wrap' }}>
                {activeVideo.caption}
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginBottom: 14 }}>
                <Stat label="Views" value={fmtCount(activeVideo.views)} />
                <Stat label="Likes" value={fmtCount(activeVideo.likes)} />
                <Stat label="Komentar" value={fmtCount(activeVideo.commentCount)} />
                <Stat label="Shares" value={fmtCount(activeVideo.shareCount)} />
              </div>

              {activeVideo.hashtags && activeVideo.hashtags.length > 0 && (
                <div>
                  <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Hashtags</div>
                  <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                    {activeVideo.hashtags.map((h, i) => (
                      <span key={i} style={{ fontSize: 11.5, padding: '3px 8px', borderRadius: 999, background: 'var(--primary-50)', color: 'var(--primary)', fontWeight: 600 }}>#{h}</span>
                    ))}
                  </div>
                </div>
              )}

              {activeVideo.authorFans != null && (
                <div style={{ marginTop: 14, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 8, fontSize: 11.5, color: 'var(--ink-2)' }}>
                  <b>Author profile:</b> @{activeVideo.author} · {fmtCount(activeVideo.authorFans)} followers{activeVideo.authorVerified ? ' · ✓ Verified' : ''}
                </div>
              )}

              <div style={{ marginTop: 14, padding: '10px 12px', background: 'oklch(0.97 0.04 75)', borderLeft: '3px solid oklch(0.55 0.16 75)', borderRadius: 8, fontSize: 11.5, color: 'var(--ink-2)', lineHeight: 1.55 }}>
                💡 <b>Tip:</b> Video dengan engagement rate tinggi (likes/views {`>`} 5%, komentar {`>`} 1%) = potensial KOL collaboration. Hashtag yang muncul = tren konten yang relevan untuk brand kamu.
              </div>
            </div>
          </div>

          {/* Comments scrape section */}
          <TikTokVideoCommentsPanel video={activeVideo} />
        </div>
      )}
    </>
  );
}

// ─── Komentar TikTok per video — scrape via Apify untuk evaluasi keluhan ───
function TikTokVideoCommentsPanel({ video }) {
  const [comments, setComments] = React.useState([]);
  const [cachedAt, setCachedAt] = React.useState(null);
  const [hasCache, setHasCache] = React.useState(false);
  const [loading, setLoading] = React.useState(false);
  const [scraping, setScraping] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [actualCount, setActualCount] = React.useState(null);
  const [maxComments, setMaxComments] = React.useState(50);
  const [filterMode, setFilterMode] = React.useState('all'); // 'all' | 'top' | '1star-keyword'
  const [search, setSearch] = React.useState('');

  // Auto-load cache saat video berubah
  React.useEffect(() => {
    if (!video?.videoId) return;
    setLoading(true); setError(null);
    setComments([]); setCachedAt(null); setHasCache(false);
    window.api.getTiktokVideoComments(video.videoId)
      .then((r) => {
        if (r.cached && r.data) {
          setComments(r.data.comments || []);
          setCachedAt(r.ageMs);
          setHasCache(true);
          setActualCount(r.data.commentsCount);
        }
      })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [video?.videoId]);

  const doScrape = async () => {
    if (!video?.videoId || !video?.url) return;
    if (!confirm(`Scrape ${maxComments} komentar dari video ini?\n\n1 Apify run, ~$0.05-0.10 tergantung actor.`)) return;
    setScraping(true); setError(null);
    try {
      const r = await window.api.fetchTiktokVideoComments(video.videoId, { videoUrl: video.url, maxComments });
      setComments(r.data?.comments || []);
      setHasCache(true);
      setCachedAt(0);
      setActualCount(r.actualScraped);
      if (r.actualScraped < maxComments * 0.5) {
        setError(`⚠ Apify return ${r.actualScraped} dari ${r.requestedMax} yang diminta — mungkin video punya komentar terbatas atau actor cap.`);
      }
    } catch (e) {
      setError(e.message || 'Gagal scrape komentar');
    } finally {
      setScraping(false);
    }
  };

  // Filter logic
  const filtered = React.useMemo(() => {
    let list = comments;
    if (filterMode === 'top') {
      list = [...list].sort((a, b) => (b.likes || 0) - (a.likes || 0));
    } else {
      // sort by date desc default
      list = [...list].sort((a, b) => (b.publishedDate || '').localeCompare(a.publishedDate || ''));
    }
    const q = search.trim().toLowerCase();
    if (q) list = list.filter((c) => c.text.toLowerCase().includes(q) || c.author.toLowerCase().includes(q));
    return list;
  }, [comments, filterMode, search]);

  return (
    <div style={{ borderTop: '1px solid var(--line)', padding: 18 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10, flexWrap: 'wrap' }}>
        <span style={{ fontSize: 14, fontWeight: 700 }}>💬 Komentar Customer</span>
        {hasCache && (
          <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)' }}>
            💾 Tersimpan · {actualCount} komentar · {cachedAt > 0 ? ageStrAudit(cachedAt) : 'baru saja'}
          </span>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
          <select
            value={maxComments}
            onChange={(e) => setMaxComments(Number(e.target.value))}
            className="select"
            style={{ fontSize: 11.5, padding: '5px 8px' }}
          >
            <option value={50}>50 komentar</option>
            <option value={100}>100 komentar</option>
            <option value={200}>200 komentar</option>
          </select>
          <button
            onClick={doScrape}
            disabled={scraping}
            className={hasCache ? 'btn btn-ghost' : 'btn btn-primary'}
            style={{ padding: '6px 12px', fontSize: 12 }}
          >
            {scraping ? 'Scraping…' : (hasCache ? '↻ Re-scrape' : '🔍 Scrape Komentar')}
          </button>
        </div>
      </div>

      <p style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 12, lineHeight: 1.55 }}>
        Ambil komentar customer untuk evaluasi keluhan, sentiment, request fitur. Apify TikTok Comments scraper, ~$0.05-0.10 per video.
      </p>

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

      {!hasCache && !loading && !scraping && (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10, lineHeight: 1.55 }}>
          <div style={{ fontSize: 28, marginBottom: 6 }}>💬</div>
          Belum ada komentar tersimpan. Klik <b>🔍 Scrape Komentar</b> untuk fetch dari TikTok.
          <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 6 }}>
            Catatan: butuh actor TikTok Comments di Super Admin → Integrasi (default <span className="mono">clockworks/tiktok-comments-scraper</span>).
          </div>
        </div>
      )}

      {loading && (
        <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>Memuat komentar tersimpan…</div>
      )}

      {hasCache && comments.length > 0 && (
        <>
          <div style={{ display: 'flex', gap: 6, marginBottom: 10, alignItems: 'center', flexWrap: 'wrap' }}>
            {[
              { id: 'all', label: 'Terbaru' },
              { id: 'top', label: '👍 Top votes' },
            ].map((opt) => (
              <button key={opt.id} onClick={() => setFilterMode(opt.id)} style={{
                padding: '5px 10px', fontSize: 11.5, fontWeight: 600, borderRadius: 6, border: 'none', cursor: 'pointer',
                background: filterMode === opt.id ? 'var(--primary)' : 'var(--bg-2)',
                color: filterMode === opt.id ? 'white' : 'var(--ink-2)',
              }}>{opt.label}</button>
            ))}
            <input
              className="input"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder='Cari kata kunci di komentar — "lambat", "harga", "lokasi"…'
              style={{ flex: 1, fontSize: 12, padding: '5px 10px' }}
            />
            {search && (
              <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{filtered.length} match</span>
            )}
          </div>

          <div style={{ maxHeight: 500, overflow: 'auto', border: '1px solid var(--line)', borderRadius: 8 }} className="scroll">
            {filtered.length === 0 ? (
              <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
                Tidak ada komentar mengandung "<b>{search}</b>".
              </div>
            ) : (
              filtered.map((c, i) => (
                <div key={c.commentId} style={{
                  padding: '10px 14px',
                  borderTop: i === 0 ? 'none' : '1px solid var(--line-2)',
                }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--primary)' }}>
                      @{c.author}{c.authorVerified && ' ✓'}
                    </span>
                    <span style={{ fontSize: 10.5, color: 'var(--ink-4)', marginLeft: 'auto' }}>
                      {c.publishedDate ? new Date(c.publishedDate).toLocaleDateString('id-ID') : ''}
                      {c.likes > 0 && ` · 👍 ${c.likes}`}
                      {c.replyCount > 0 && ` · 💬 ${c.replyCount} reply`}
                    </span>
                  </div>
                  <div style={{ fontSize: 12.5, color: 'var(--ink)', lineHeight: 1.55, whiteSpace: 'pre-wrap' }}>
                    {search.trim() ? highlightMatch(c.text, search.toLowerCase()) : c.text}
                  </div>
                </div>
              ))
            )}
          </div>
        </>
      )}
    </div>
  );
}

// ─── Tab: Instagram Insight (via Apify) ─────────────────────────────
// Mirror dari SuperAdminTikTokInsight. Backend response shape sudah
// punya alias videoCount/topVideosByViews/etc supaya reuse TikTokDashboardView
// dan TikTokCompareView tanpa modifikasi.
function SuperAdminInstagramInsight() {
  const [mode, setMode] = React.useState('search');
  const [searchType, setSearchType] = React.useState('hashtag'); // 'hashtag' | 'profile-reels'
  const [dateRange, setDateRange] = React.useState('all');
  const [maxItems, setMaxItems] = React.useState(25);
  const [compareBrands, setCompareBrands] = React.useState([]);
  const [compareData, setCompareData] = React.useState(null);
  const [compareLoading, setCompareLoading] = React.useState(false);
  const [brand, setBrand] = React.useState('');
  const [posts, setPosts] = React.useState([]);
  const [searching, setSearching] = React.useState(false);
  const [searchCached, setSearchCached] = React.useState(false);
  const [searchAge, setSearchAge] = React.useState(0);
  const [history, setHistory] = React.useState([]);
  const [activePost, setActivePost] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [actorId, setActorId] = React.useState(null);
  const [dashboard, setDashboard] = React.useState(null);
  const [dashLoading, setDashLoading] = React.useState(false);
  const [searchTotalCount, setSearchTotalCount] = React.useState(0);
  const [reportData, setReportData] = React.useState(null);
  const [reportLoading, setReportLoading] = React.useState(false);

  React.useEffect(() => {
    window.api.listInstagramSearches(searchType).then((r) => setHistory(r.searches || [])).catch(() => {});
  }, [searchType]);

  // Reset state saat ganti searchType — supaya gak campur data hashtag vs reels
  React.useEffect(() => {
    setBrand('');
    setPosts([]);
    setActivePost(null);
    setDashboard(null);
    setCompareBrands([]);
    setCompareData(null);
    setError(null);
  }, [searchType]);

  const loadDashboard = async (b, range) => {
    if (!b) return;
    setDashLoading(true); setError(null);
    try {
      const r = await window.api.instagramDashboard(b, range || dateRange, searchType);
      setDashboard(r);
    } catch (e) {
      setError(e.message || 'Gagal load dashboard');
      setDashboard(null);
    } finally { setDashLoading(false); }
  };

  const runCompare = async (range) => {
    if (compareBrands.length < 2) return;
    setCompareLoading(true); setError(null);
    try {
      const r = await window.api.instagramCompare(compareBrands, range || dateRange, searchType);
      setCompareData(r);
    } catch (e) {
      setError(e.message || 'Gagal compare');
      setCompareData(null);
    } finally { setCompareLoading(false); }
  };

  const doSearch = async (force, range) => {
    if (!brand.trim()) return;
    if (force) {
      const estCost = maxItems <= 25 ? '$0.01-0.10' : maxItems <= 50 ? '$0.02-0.20' : maxItems <= 100 ? '$0.05-0.40' : '$0.10-0.80';
      const label = searchType === 'profile-reels' ? `${maxItems} reel` : `${maxItems} post`;
      if (!confirm(`Re-scan Instagram ${label}?\n\nEstimasi cost Apify: ${estCost}`)) return;
    }
    setSearching(true); setError(null); setActivePost(null);
    try {
      const r = await window.api.instagramSearch({ brand: brand.trim(), force: !!force, dateRange: range || dateRange, maxItems, searchType });
      setPosts(r.posts || []);
      setSearchCached(!!r.cached);
      setSearchAge(r.ageMs || 0);
      setActorId(r.actor || null);
      setSearchTotalCount(r.totalCount || (r.posts || []).length);
      if (!r.cached && r.requestedMaxItems && r.actualScraped != null && r.actualScraped < r.requestedMaxItems * 0.6) {
        const label = searchType === 'profile-reels' ? 'reel' : 'post';
        setError(`⚠ Apify return ${r.actualScraped} ${label} dari ${r.requestedMaxItems} yang diminta. Kemungkinan: (1) actor cap di plan kamu, (2) profile/hashtag tidak punya cukup konten, (3) ganti actor di Integrasi.`);
      }
      window.api.listInstagramSearches(searchType).then((rr) => setHistory(rr.searches || []));
    } catch (e) {
      setError(e.message || 'Gagal cari post Instagram');
    } finally { setSearching(false); }
  };

  React.useEffect(() => {
    if (mode === 'dashboard' && brand.trim() && dashboard) loadDashboard(brand.trim(), dateRange);
    else if (mode === 'compare' && compareBrands.length >= 2 && compareData) runCompare(dateRange);
    else if (mode === 'search' && brand.trim() && posts.length > 0) doSearch(false, dateRange);
  }, [dateRange]);

  React.useEffect(() => {
    if (mode !== 'dashboard') return;
    const target = brand.trim() || (history[0]?.brandKey ?? '');
    if (target) {
      if (!brand.trim()) setBrand(target);
      loadDashboard(target);
    }
  }, [mode, history.length]);

  const toggleCompareBrand = (brandKey) => {
    setCompareBrands((prev) => {
      if (prev.includes(brandKey)) return prev.filter((b) => b !== brandKey);
      if (prev.length >= 5) return prev;
      return [...prev, brandKey];
    });
    setCompareData(null);
  };

  const fmtCount = (n) => {
    if (n == null) return '—';
    if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
    if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
    return String(n);
  };

  const deleteSearch = async (brandKey) => {
    if (!confirm(`Hapus search untuk "${brandKey}"?\n\nCached data akan dihapus permanen (perlu re-scrape Apify kalau mau lihat lagi).`)) return;
    try {
      await window.api.deleteInstagramSearch(brandKey, searchType);
      const rr = await window.api.listInstagramSearches(searchType);
      setHistory(rr.searches || []);
      if (brand.trim().toLowerCase() === brandKey.toLowerCase()) {
        setBrand(''); setPosts([]); setDashboard(null); setActivePost(null);
      }
      setCompareBrands((prev) => prev.filter((b) => b.toLowerCase() !== brandKey.toLowerCase()));
    } catch (e) {
      setError(e.message || 'Gagal hapus search');
    }
  };

  const exportReport = async () => {
    if (!dashboard || !brand.trim()) return;
    setReportLoading(true); setError(null);
    try {
      const stats = dashboard.stats || {};
      const payload = {
        brand: brand.trim(),
        dateRange,
        searchType,
        stats: {
          itemCount: stats.postCount || stats.videoCount || 0,
          totalReach: stats.totalViews || stats.totalLikes || 0,
          totalLikes: stats.totalLikes || 0,
          totalComments: stats.totalComments || 0,
          totalShares: 0,
          avgEngagementRate: stats.avgEngagementRate || 0,
        },
        topCreators: (dashboard.topCreators || []).slice(0, 8).map((c) => ({
          author: c.author, fans: c.fans, posts: c.posts,
          totalReach: c.totalViews || c.totalLikes || 0,
          totalLikes: c.totalLikes || 0,
          engagementRate: c.avgEngagementRate || 0,
        })),
        topHashtags: (dashboard.topHashtags || []).slice(0, 10).map((h) => ({
          tag: h.tag, freq: h.freq, totalReach: h.totalReach || h.totalViews || 0,
        })),
        audienceTiers: dashboard.audienceTiers || { nano: 0, micro: 0, mid: 0, macro: 0, mega: 0, total: 0 },
        topContent: (dashboard.topVideosByViews || []).slice(0, 6).map((v) => ({
          caption: v.caption || '', author: v.author || v.ownerUsername || '',
          views: v.views || v.likes || 0, likes: v.likes || 0,
          commentCount: v.commentCount || 0, url: v.url || '',
        })),
        weeklyTimeline: (dashboard.weeklyTimeline || []).slice(-8),
        comments: dashboard.commentStats && dashboard.commentStats.totalScraped > 0 && dashboard.commentAi ? {
          totalScraped: dashboard.commentStats.totalScraped,
          coverage: dashboard.commentStats.coverage || 0,
          overallSentiment: dashboard.commentAi.overallSentiment || null,
          complaints: dashboard.commentAi.complaints || [],
          praises: dashboard.commentAi.praises || [],
        } : null,
      };
      const r = await window.api.instagramReportNarrative(payload);
      setReportData({
        platform: 'instagram',
        brand: brand.trim(),
        dateRange,
        searchType,
        dashboard,
        narrative: r.narrative,
        generatedAt: new Date(),
      });
    } catch (e) {
      setError(e.message || 'Gagal generate laporan');
    } finally {
      setReportLoading(false);
    }
  };

  return (
    <>
      <div className="card" style={{ padding: 22, marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
          <span style={{ fontSize: 16 }}>📷</span>
          <h2 style={{ margin: 0, fontSize: 17, fontWeight: 600 }}>Instagram Insight</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 style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
            {/* Search type toggle: Hashtag / Profile Reels */}
            <div style={{ display: 'inline-flex', borderRadius: 6, overflow: 'hidden', border: '1px solid var(--line)' }}>
              <button onClick={() => setSearchType('hashtag')} style={{
                padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: searchType === 'hashtag' ? 'oklch(0.55 0.18 320)' : 'var(--bg)',
                color: searchType === 'hashtag' ? 'white' : 'var(--ink-2)',
              }}>🏷 Hashtag</button>
              <button onClick={() => setSearchType('profile-reels')} style={{
                padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: searchType === 'profile-reels' ? 'oklch(0.55 0.18 320)' : 'var(--bg)',
                color: searchType === 'profile-reels' ? 'white' : 'var(--ink-2)',
                borderLeft: '1px solid var(--line)',
              }}>🎬 Profile Reels</button>
            </div>
            {(mode === 'dashboard' || mode === 'compare' || mode === 'search') && (
              <div style={{ display: 'inline-flex', borderRadius: 6, overflow: 'hidden', border: '1px solid var(--line)' }}>
                {[
                  { id: 'all', label: 'Semua' },
                  { id: '90d', label: '90 hari' },
                  { id: '30d', label: '30 hari' },
                  { id: '7d', label: '7 hari' },
                ].map((opt) => (
                  <button key={opt.id} onClick={() => setDateRange(opt.id)} style={{
                    padding: '5px 9px', fontSize: 11, fontWeight: 600, border: 'none', cursor: 'pointer',
                    background: dateRange === opt.id ? 'var(--primary)' : 'var(--bg)',
                    color: dateRange === opt.id ? 'white' : 'var(--ink-2)',
                    borderLeft: opt.id !== 'all' ? '1px solid var(--line)' : 'none',
                  }}>{opt.label}</button>
                ))}
              </div>
            )}
            <div style={{ display: 'inline-flex', borderRadius: 8, overflow: 'hidden', border: '1px solid var(--line)', background: 'var(--bg-2)' }}>
              <button onClick={() => setMode('search')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'search' ? 'var(--bg)' : 'transparent',
                color: mode === 'search' ? 'var(--ink)' : 'var(--ink-3)',
                borderRight: '1px solid var(--line)',
              }}>🔎 Search</button>
              <button onClick={() => setMode('dashboard')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'dashboard' ? 'var(--bg)' : 'transparent',
                color: mode === 'dashboard' ? 'var(--ink)' : 'var(--ink-3)',
                borderRight: '1px solid var(--line)',
              }}>📊 Dashboard</button>
              <button onClick={() => setMode('compare')} style={{
                padding: '6px 14px', fontSize: 12, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === 'compare' ? 'var(--bg)' : 'transparent',
                color: mode === 'compare' ? 'var(--ink)' : 'var(--ink-3)',
              }}>⚖️ Compare</button>
            </div>
          </div>
        </div>
        <p style={{ margin: '0 0 14px', fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.55 }}>
          {mode === 'search'
            ? (searchType === 'profile-reels'
                ? <>Scrape <b>Reels</b> dari profile Instagram (kompetitor / KOL). Input multi username pakai koma. <b>Cost</b>: 1 Apify run per search.</>
                : <>Scrape post Instagram via <b>hashtag</b> brand → list post + creator + stats. <b>Cost</b>: 1 Apify run per search.</>
              )
            : mode === 'dashboard'
            ? <>Analytics dashboard dari data {searchType === 'profile-reels' ? 'Reels' : 'hashtag'} — KPI, top performer, KOL identification, AI strategic insight. <b>0 Apify run</b>.</>
            : <>Bandingkan TOM antar brand di Instagram ({searchType === 'profile-reels' ? 'Reels' : 'Hashtag'}) — Share of Voice, Reach, Engagement. Pilih 2-5 brand. <b>0 Apify run</b>.</>
          }
        </p>

        {mode !== 'compare' ? (
          <div style={{ display: 'grid', gridTemplateColumns: mode === 'search' ? '1fr auto auto' : '1fr auto', gap: 10, alignItems: 'flex-end' }}>
            <div>
              <label className="label">
                {searchType === 'profile-reels' ? 'Username Profile' : 'Brand / Hashtag'}
                {mode === 'search' && <span style={{ fontWeight: 400, color: 'var(--ink-3)' }}> · multi pakai koma</span>}
              </label>
              <input
                className="input"
                value={brand}
                onChange={(e) => setBrand(e.target.value)}
                placeholder={mode === 'search'
                  ? (searchType === 'profile-reels' ? "@geprekinaja, geprekin.chicken, @karirgeprekinaja" : "geprekinaja, ayamgeprek, geprekincirebon")
                  : (searchType === 'profile-reels' ? "@geprekinaja" : "Geprekin Aja")
                }
                onKeyDown={(e) => e.key === 'Enter' && (mode === 'search' ? doSearch(false) : loadDashboard(brand.trim()))}
              />
              {mode === 'search' && (
                <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 4 }}>
                  {searchType === 'profile-reels'
                    ? <>Input username profile Instagram (boleh pakai @). Akan scrape Reels dari masing-masing akun.</>
                    : <>Tip: pisah pakai koma untuk scrape multi hashtag — IG cap ~25 post per hashtag, multi-tag = lebih banyak post.</>
                  }
                </div>
              )}
            </div>
            {mode === 'search' && (
              <div>
                <label className="label">{searchType === 'profile-reels' ? 'Jumlah Reel' : 'Jumlah Post'}</label>
                <select
                  className="select"
                  value={maxItems}
                  onChange={(e) => setMaxItems(Number(e.target.value))}
                  style={{ fontSize: 12.5, minWidth: 110 }}
                >
                  <option value={25}>25 {searchType === 'profile-reels' ? 'reel' : 'post'}</option>
                  <option value={50}>50 {searchType === 'profile-reels' ? 'reel' : 'post'}</option>
                  <option value={100}>100 {searchType === 'profile-reels' ? 'reel' : 'post'}</option>
                  <option value={200}>200 {searchType === 'profile-reels' ? 'reel' : 'post'}</option>
                </select>
              </div>
            )}
            <button
              className="btn btn-primary"
              onClick={() => mode === 'search' ? doSearch(false) : loadDashboard(brand.trim())}
              disabled={!brand.trim() || searching || dashLoading}
              style={{ padding: '10px 18px' }}
            >
              {mode === 'search'
                ? (searching ? `Scraping ${maxItems}…` : `🔎 Cari ${maxItems} ${searchType === 'profile-reels' ? 'Reel' : 'Post'}`)
                : (dashLoading ? 'Loading…' : '📊 Buka Dashboard')
              }
            </button>
          </div>
        ) : (
          <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>
            👇 Pilih brand dari grid (min 2, max 5), lalu klik <b>Bandingkan</b>.
          </div>
        )}
      </div>

      {error && (
        <div style={{ padding: 12, borderRadius: 10, background: 'var(--danger-soft)', color: 'var(--danger)', fontSize: 13, marginBottom: 12, lineHeight: 1.55 }}>
          <b>Gagal:</b> {error}
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 6 }}>
            Pastikan: (1) Apify token tersimpan & valid di tab Integrasi, (2) Apify Actor ID terisi (default: <span className="mono">apify/instagram-scraper</span>) atau pilih actor lain dari <a href="https://apify.com/store?search=instagram" target="_blank" rel="noreferrer" style={{ color: 'var(--primary)' }}>Apify Store</a>, (3) akun Apify punya credit cukup.
          </div>
        </div>
      )}

      {mode === 'dashboard' && history.length > 0 && (
        <div className="card" style={{ padding: 14, marginBottom: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 6 }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
              💾 Brand Tersimpan ({history.length}) — klik untuk buka dashboard
            </span>
            <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>0 Apify call · read DB only</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 8 }}>
            {history.map((h, i) => {
              const isActive = brand.trim().toLowerCase() === h.brandKey.toLowerCase();
              return (
                <div
                  key={i}
                  onClick={() => { setBrand(h.brandKey); loadDashboard(h.brandKey); }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px',
                    background: isActive ? 'var(--primary-50)' : 'var(--bg)',
                    border: `1px solid ${isActive ? 'var(--primary)' : 'var(--line)'}`,
                    borderRadius: 8, cursor: 'pointer', textAlign: 'left', transition: 'all 120ms',
                  }}
                >
                  <span style={{ fontSize: 18 }}>📷</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: isActive ? 'var(--primary)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {h.brandKey}
                    </div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>
                      {ageStrAudit(Date.now() - new Date(h.fetchedAt).getTime())}
                    </div>
                  </div>
                  {isActive && <span style={{ fontSize: 14, color: 'var(--primary)' }}>●</span>}
                  <button
                    onClick={(e) => { e.stopPropagation(); deleteSearch(h.brandKey); }}
                    title="Hapus search"
                    style={{
                      width: 22, height: 22, padding: 0, lineHeight: 1,
                      background: 'transparent', border: '1px solid var(--line)', borderRadius: 6,
                      color: 'var(--ink-3)', fontSize: 14, cursor: 'pointer',
                    }}
                    onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--danger)'; e.currentTarget.style.color = 'var(--danger)'; }}
                    onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                  >×</button>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {mode === 'dashboard' && dashLoading && (
        <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>Memuat analytics dashboard…</div>
      )}
      {mode === 'dashboard' && dashboard && (
        <>
          <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }}>
            <button
              onClick={exportReport}
              disabled={reportLoading}
              className="btn"
              style={{
                padding: '8px 16px', fontSize: 12.5, fontWeight: 600,
                background: 'oklch(0.32 0.05 250)', color: 'white', border: 'none',
                borderRadius: 8, cursor: reportLoading ? 'wait' : 'pointer',
                boxShadow: '0 1px 2px rgba(0,0,0,0.1)',
              }}
              title="Generate laporan profesional untuk BOD/investor (2 AI call, ~10-20 detik)"
            >
              {reportLoading ? '📄 Menyusun laporan AI…' : '📄 Export Laporan PDF'}
            </button>
          </div>
          <TikTokDashboardView data={dashboard} />
        </>
      )}
      {mode === 'dashboard' && !dashboard && !dashLoading && history.length === 0 && (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10 }}>
          Belum ada data brand tersimpan. Switch ke <b>🔎 Search</b> untuk scrape brand pertama.
        </div>
      )}

      {reportData && window.SocialReportView && (
        <window.SocialReportView data={reportData} onClose={() => setReportData(null)} />
      )}

      {mode === 'compare' && (
        <>
          {history.length < 2 ? (
            <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10 }}>
              Butuh minimal <b>2 brand</b> tersimpan. Switch ke <b>🔎 Search</b> dulu.
            </div>
          ) : (
            <>
              <div className="card" style={{ padding: 14, marginBottom: 14 }}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 6 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                    ⚖️ Pilih Brand untuk Dibandingkan ({compareBrands.length}/5)
                  </span>
                  <button className="btn btn-primary" onClick={() => runCompare()} disabled={compareBrands.length < 2 || compareLoading} style={{ padding: '6px 14px', fontSize: 12 }}>
                    {compareLoading ? 'Computing…' : `📊 Bandingkan ${compareBrands.length} Brand`}
                  </button>
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 8 }}>
                  {history.map((h, i) => {
                    const checked = compareBrands.includes(h.brandKey);
                    const order = compareBrands.indexOf(h.brandKey) + 1;
                    return (
                      <button key={i} onClick={() => toggleCompareBrand(h.brandKey)}
                        disabled={!checked && compareBrands.length >= 5}
                        style={{
                          display: 'grid', gridTemplateColumns: '20px 1fr auto', gap: 8, alignItems: 'center', padding: '10px 12px',
                          background: checked ? 'var(--primary-50)' : 'var(--bg)',
                          border: `1px solid ${checked ? 'var(--primary)' : 'var(--line)'}`,
                          borderRadius: 8, cursor: 'pointer', textAlign: 'left',
                          opacity: !checked && compareBrands.length >= 5 ? 0.4 : 1,
                        }}>
                        <input type="checkbox" checked={checked} readOnly style={{ pointerEvents: 'none' }} />
                        <span style={{ fontSize: 13, fontWeight: 700, color: checked ? 'var(--primary)' : 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                          {h.brandKey}
                        </span>
                        {checked && (
                          <span className="mono" style={{ fontSize: 10.5, padding: '2px 6px', borderRadius: 999, background: 'var(--primary)', color: 'white', fontWeight: 700 }}>#{order}</span>
                        )}
                      </button>
                    );
                  })}
                </div>
              </div>
              {compareLoading && <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>Menghitung perbandingan…</div>}
              {compareData && <TikTokCompareView data={compareData} />}
            </>
          )}
        </>
      )}

      {mode === 'search' && history.length > 0 && posts.length === 0 && !activePost && (
        <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 }}>
            Search terakhir
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {history.slice(0, 8).map((h, i) => (
              <div key={i} onClick={() => { setBrand(h.brandKey); setTimeout(() => doSearch(false), 0); }}
                style={{
                  display: 'grid', gridTemplateColumns: '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 }}>{h.brandKey}</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{new Date(h.fetchedAt).toLocaleDateString('id-ID')}</span>
                <button
                  onClick={(e) => { e.stopPropagation(); deleteSearch(h.brandKey); }}
                  title="Hapus search"
                  style={{
                    width: 22, height: 22, padding: 0, lineHeight: 1,
                    background: 'transparent', border: '1px solid var(--line)', borderRadius: 6,
                    color: 'var(--ink-3)', fontSize: 14, cursor: 'pointer',
                  }}
                  onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--danger)'; e.currentTarget.style.color = 'var(--danger)'; }}
                  onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
                >×</button>
              </div>
            ))}
          </div>
        </div>
      )}

      {mode === 'search' && posts.length === 0 && searchTotalCount > 0 && dateRange !== 'all' && !searching && (
        <div className="card" style={{ padding: 24, textAlign: 'center' }}>
          <div style={{ fontSize: 32, marginBottom: 8 }}>📅</div>
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-2)', marginBottom: 4 }}>
            Tidak ada post di filter "{dateRange === '7d' ? '7 hari' : dateRange === '30d' ? '30 hari' : '90 hari'} terakhir"
          </div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginBottom: 12 }}>
            Total {searchTotalCount} post tersimpan, tapi tidak ada yang cocok dengan rentang tanggal.
          </div>
          <button onClick={() => setDateRange('all')} className="btn btn-ghost" style={{ fontSize: 11.5 }}>Lihat Semua Waktu</button>
        </div>
      )}

      {mode === 'search' && posts.length > 0 && !activePost && (
        <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' }}>
              {posts.length} {searchType === 'profile-reels' ? 'Reel' : 'Post'}
              {dateRange !== 'all' && searchTotalCount > posts.length && (
                <span style={{ fontWeight: 500, opacity: 0.7, marginLeft: 4, textTransform: 'none', letterSpacing: 0 }}>
                  (dari {searchTotalCount})
                </span>
              )}
            </span>
            {dateRange !== 'all' && (
              <span style={{
                fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700,
                background: 'var(--primary-50)', color: 'var(--primary)',
                textTransform: 'uppercase', letterSpacing: '0.04em',
              }}>📅 {dateRange === '7d' ? '7 hari' : dateRange === '30d' ? '30 hari' : '90 hari'} terakhir</span>
            )}
            {searchCached ? (
              <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)' }}>
                💾 Tersimpan · {ageStrAudit(searchAge)}
              </span>
            ) : (
              <span style={{ fontSize: 10.5, padding: '2px 8px', borderRadius: 999, fontWeight: 700, background: 'var(--primary-50)', color: 'var(--primary)' }}>
                ✨ Fresh{actorId && ` · ${actorId}`}
              </span>
            )}
            <button onClick={() => doSearch(true)} disabled={searching} className="btn btn-ghost"
              style={{ marginLeft: 'auto', padding: '4px 10px', fontSize: 11.5 }}>↻ Re-scan</button>
          </div>
          <div>
            {posts.map((p) => (
              <button key={p.postId} onClick={() => setActivePost(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: '90px 1fr 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)'}>
                {p.thumbnail ? (
                  <img src={p.thumbnail} alt="" referrerPolicy="no-referrer" style={{ width: 90, height: 90, objectFit: 'cover', borderRadius: 6, background: 'var(--bg-2)' }} />
                ) : (
                  <div style={{ width: 90, height: 90, background: 'linear-gradient(135deg, oklch(0.78 0.16 320), oklch(0.82 0.16 50))', borderRadius: 6, display: 'grid', placeItems: 'center', color: 'white', fontSize: 28 }}>📷</div>
                )}
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--primary)' }}>@{p.ownerUsername}</span>
                    {p.ownerVerified && <span style={{ fontSize: 10, color: 'var(--primary)' }}>✓</span>}
                    {p.ownerFollowers != null && (
                      <span style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>· {fmtCount(p.ownerFollowers)} followers</span>
                    )}
                    {p.isVideo && <span style={{ fontSize: 9.5, padding: '1px 5px', borderRadius: 4, background: 'oklch(0.94 0.03 280)', color: 'oklch(0.45 0.18 280)', fontWeight: 700 }}>REEL/VIDEO</span>}
                  </div>
                  <div style={{ fontSize: 12.5, color: 'var(--ink)', marginBottom: 6, lineHeight: 1.4, maxWidth: 460 }}>
                    {(p.caption || '').length > 140 ? (p.caption || '').slice(0, 139) + '…' : (p.caption || '—')}
                  </div>
                  <div style={{ display: 'flex', gap: 12, fontSize: 11, color: 'var(--ink-3)', flexWrap: 'wrap' }}>
                    {p.views > 0 && <span>👁 {fmtCount(p.views)}</span>}
                    <span>❤️ {fmtCount(p.likes)}</span>
                    <span>💬 {fmtCount(p.commentCount)}</span>
                    {p.publishedDate && <span>· {new Date(p.publishedDate).toLocaleDateString('id-ID')}</span>}
                  </div>
                  {p.hashtags && p.hashtags.length > 0 && (
                    <div style={{ display: 'flex', gap: 4, marginTop: 6, flexWrap: 'wrap' }}>
                      {p.hashtags.slice(0, 5).map((h, i) => (
                        <span key={i} style={{ fontSize: 10, padding: '1px 6px', borderRadius: 999, background: 'var(--primary-50)', color: 'var(--primary)', fontWeight: 600 }}>#{h}</span>
                      ))}
                    </div>
                  )}
                </div>
                <span style={{ fontSize: 11.5, color: 'var(--primary)', fontWeight: 600 }}>Detail →</span>
              </button>
            ))}
          </div>
        </div>
      )}

      {mode === 'search' && activePost && (
        <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={() => setActivePost(null)} className="btn btn-ghost" style={{ padding: '5px 10px', fontSize: 12 }}>← Kembali</button>
            <div style={{ flex: 1, minWidth: 200 }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--primary)' }}>@{activePost.ownerUsername}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
                {activePost.publishedDate && new Date(activePost.publishedDate).toLocaleDateString('id-ID')}
                {activePost.type && ` · ${activePost.type}`}
              </div>
            </div>
            <a href={activePost.url} target="_blank" rel="noreferrer" className="btn btn-primary" style={{ padding: '6px 14px', fontSize: 12, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              📷 Buka di Instagram
            </a>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '240px 1fr', gap: 18, padding: 18 }}>
            {activePost.thumbnail ? (
              <img src={activePost.thumbnail} alt="" referrerPolicy="no-referrer" style={{ width: 240, height: 240, objectFit: 'cover', borderRadius: 10, background: 'var(--bg-2)' }} />
            ) : (
              <div style={{ width: 240, height: 240, background: 'linear-gradient(135deg, oklch(0.78 0.16 320), oklch(0.82 0.16 50))', borderRadius: 10, display: 'grid', placeItems: 'center', color: 'white', fontSize: 60 }}>📷</div>
            )}
            <div>
              <div style={{ fontSize: 13.5, color: 'var(--ink)', lineHeight: 1.6, marginBottom: 14, whiteSpace: 'pre-wrap' }}>
                {activePost.caption || '—'}
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8, marginBottom: 14 }}>
                {activePost.views > 0 && <Stat label="Views" value={fmtCount(activePost.views)} />}
                <Stat label="Likes" value={fmtCount(activePost.likes)} />
                <Stat label="Komentar" value={fmtCount(activePost.commentCount)} />
              </div>

              {activePost.hashtags && activePost.hashtags.length > 0 && (
                <div>
                  <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Hashtags</div>
                  <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                    {activePost.hashtags.map((h, i) => (
                      <span key={i} style={{ fontSize: 11.5, padding: '3px 8px', borderRadius: 999, background: 'var(--primary-50)', color: 'var(--primary)', fontWeight: 600 }}>#{h}</span>
                    ))}
                  </div>
                </div>
              )}

              {activePost.ownerFollowers != null && (
                <div style={{ marginTop: 14, padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 8, fontSize: 11.5, color: 'var(--ink-2)' }}>
                  <b>Author:</b> @{activePost.ownerUsername} · {fmtCount(activePost.ownerFollowers)} followers{activePost.ownerVerified ? ' · ✓ Verified' : ''}
                </div>
              )}

              <div style={{ marginTop: 14, padding: '10px 12px', background: 'oklch(0.97 0.04 75)', borderLeft: '3px solid oklch(0.55 0.16 75)', borderRadius: 8, fontSize: 11.5, color: 'var(--ink-2)', lineHeight: 1.55 }}>
                💡 <b>Tip:</b> Engagement rate IG benchmark 1-3%. Reel umumnya reach lebih luas dari Image post. Creator dengan ER tinggi = potensi KOL.
              </div>
            </div>
          </div>

          <InstagramPostCommentsPanel post={activePost} />
        </div>
      )}
    </>
  );
}

// ─── Komentar Instagram per post ──────────────────────────────────
function InstagramPostCommentsPanel({ post }) {
  const [comments, setComments] = React.useState([]);
  const [cachedAt, setCachedAt] = React.useState(null);
  const [hasCache, setHasCache] = React.useState(false);
  const [loading, setLoading] = React.useState(false);
  const [scraping, setScraping] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [actualCount, setActualCount] = React.useState(null);
  const [maxComments, setMaxComments] = React.useState(50);
  const [filterMode, setFilterMode] = React.useState('all');
  const [search, setSearch] = React.useState('');

  React.useEffect(() => {
    if (!post?.postId) return;
    setLoading(true); setError(null);
    setComments([]); setCachedAt(null); setHasCache(false);
    window.api.getInstagramPostComments(post.postId)
      .then((r) => {
        if (r.cached && r.data) {
          setComments(r.data.comments || []);
          setCachedAt(r.ageMs);
          setHasCache(true);
          setActualCount(r.data.commentsCount);
        }
      })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [post?.postId]);

  const doScrape = async () => {
    if (!post?.postId || !post?.url) return;
    if (!confirm(`Scrape ${maxComments} komentar dari post ini?\n\n1 Apify run, ~$0.05-0.10.`)) return;
    setScraping(true); setError(null);
    try {
      const r = await window.api.fetchInstagramPostComments(post.postId, { postUrl: post.url, maxComments });
      setComments(r.data?.comments || []);
      setHasCache(true);
      setCachedAt(0);
      setActualCount(r.actualScraped);
      if (r.actualScraped < maxComments * 0.5) {
        setError(`⚠ Apify return ${r.actualScraped} dari ${r.requestedMax} yang diminta — mungkin post punya komentar terbatas atau actor cap.`);
      }
    } catch (e) {
      setError(e.message || 'Gagal scrape komentar');
    } finally { setScraping(false); }
  };

  const filtered = React.useMemo(() => {
    let list = comments;
    if (filterMode === 'top') list = [...list].sort((a, b) => (b.likes || 0) - (a.likes || 0));
    else list = [...list].sort((a, b) => (b.publishedDate || '').localeCompare(a.publishedDate || ''));
    const q = search.trim().toLowerCase();
    if (q) list = list.filter((c) => c.text.toLowerCase().includes(q) || c.author.toLowerCase().includes(q));
    return list;
  }, [comments, filterMode, search]);

  return (
    <div style={{ borderTop: '1px solid var(--line)', padding: 18 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10, flexWrap: 'wrap' }}>
        <span style={{ fontSize: 14, fontWeight: 700 }}>💬 Komentar Customer</span>
        {hasCache && (
          <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)' }}>
            💾 Tersimpan · {actualCount} komentar · {cachedAt > 0 ? ageStrAudit(cachedAt) : 'baru saja'}
          </span>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6, alignItems: 'center' }}>
          <select value={maxComments} onChange={(e) => setMaxComments(Number(e.target.value))} className="select" style={{ fontSize: 11.5, padding: '5px 8px' }}>
            <option value={50}>50 komentar</option>
            <option value={100}>100 komentar</option>
            <option value={200}>200 komentar</option>
          </select>
          <button onClick={doScrape} disabled={scraping} className={hasCache ? 'btn btn-ghost' : 'btn btn-primary'} style={{ padding: '6px 12px', fontSize: 12 }}>
            {scraping ? 'Scraping…' : (hasCache ? '↻ Re-scrape' : '🔍 Scrape Komentar')}
          </button>
        </div>
      </div>

      <p style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 12, lineHeight: 1.55 }}>
        Ambil komentar customer untuk evaluasi keluhan, sentiment, request fitur. Apify Instagram Comment scraper, ~$0.05-0.10 per post.
      </p>

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

      {!hasCache && !loading && !scraping && (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, background: 'var(--bg-2)', borderRadius: 10, lineHeight: 1.55 }}>
          <div style={{ fontSize: 28, marginBottom: 6 }}>💬</div>
          Belum ada komentar tersimpan. Klik <b>🔍 Scrape Komentar</b> untuk fetch dari Instagram.
          <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 6 }}>
            Catatan: butuh actor Instagram Comments di Super Admin → Integrasi (default <span className="mono">apify/instagram-comment-scraper</span>).
          </div>
        </div>
      )}

      {loading && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>Memuat komentar tersimpan…</div>}

      {hasCache && comments.length > 0 && (
        <>
          <div style={{ display: 'flex', gap: 6, marginBottom: 10, alignItems: 'center', flexWrap: 'wrap' }}>
            {[
              { id: 'all', label: 'Terbaru' },
              { id: 'top', label: '👍 Top votes' },
            ].map((opt) => (
              <button key={opt.id} onClick={() => setFilterMode(opt.id)} style={{
                padding: '5px 10px', fontSize: 11.5, fontWeight: 600, borderRadius: 6, border: 'none', cursor: 'pointer',
                background: filterMode === opt.id ? 'var(--primary)' : 'var(--bg-2)',
                color: filterMode === opt.id ? 'white' : 'var(--ink-2)',
              }}>{opt.label}</button>
            ))}
            <input className="input" value={search} onChange={(e) => setSearch(e.target.value)}
              placeholder='Cari kata kunci di komentar — "lambat", "harga", "lokasi"…'
              style={{ flex: 1, fontSize: 12, padding: '5px 10px' }} />
            {search && <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{filtered.length} match</span>}
          </div>

          <div style={{ maxHeight: 500, overflow: 'auto', border: '1px solid var(--line)', borderRadius: 8 }} className="scroll">
            {filtered.length === 0 ? (
              <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
                Tidak ada komentar mengandung "<b>{search}</b>".
              </div>
            ) : (
              filtered.map((c, i) => (
                <div key={c.commentId} style={{ padding: '10px 14px', borderTop: i === 0 ? 'none' : '1px solid var(--line-2)' }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 4, flexWrap: 'wrap' }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--primary)' }}>
                      @{c.author}{c.authorVerified && ' ✓'}
                    </span>
                    <span style={{ fontSize: 10.5, color: 'var(--ink-4)', marginLeft: 'auto' }}>
                      {c.publishedDate ? new Date(c.publishedDate).toLocaleDateString('id-ID') : ''}
                      {c.likes > 0 && ` · 👍 ${c.likes}`}
                      {c.replyCount > 0 && ` · 💬 ${c.replyCount} reply`}
                    </span>
                  </div>
                  <div style={{ fontSize: 12.5, color: 'var(--ink)', lineHeight: 1.55, whiteSpace: 'pre-wrap' }}>
                    {search.trim() ? highlightMatch(c.text, search.toLowerCase()) : c.text}
                  </div>
                </div>
              ))
            )}
          </div>
        </>
      )}
    </div>
  );
}

// ─── SocialInsight wrapper — TikTok + Instagram tabs ────────────────
function SocialInsightView() {
  const [tab, setTab] = React.useState('tiktok'); // 'tiktok' | 'instagram'
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <TopBar
        title="Social Insight"
        subtitle={
          tab === 'tiktok'
            ? 'TikTok — analisis video viral tentang brand (via Apify)'
            : 'Instagram — analisis post & creator tentang brand (via Apify)'
        }
      />
      <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 === 'tiktok' ? 'active' : ''}`} onClick={() => setTab('tiktok')}>
              🎵 TikTok <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 === 'instagram' ? 'active' : ''}`} onClick={() => setTab('instagram')}>
              📷 Instagram <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 === 'tiktok' && <SuperAdminTikTokInsight />}
          {tab === 'instagram' && <SuperAdminInstagramInsight />}
        </div>
      </div>
    </div>
  );
}

window.SocialInsightView = SocialInsightView;
