// Google Trends preview panel — connects to /api/campaigns/:id/trends
// (powered by SerpAPI). When SerpAPI key isn't configured, falls back to
// demo data with a "PREVIEW" badge so the UI is always visible.

const TRENDS_DEMO = {
  brand: 'Kopi Kenanga',
  period: '90 hari terakhir (demo)',
  series: [
    { name: 'Kopi Kenanga',     color: 'var(--primary)',           isUs: true,  points: [22,26,31,28,35,42,48,52,58,64,71,78] },
    { name: 'Kopi Janji Manis', color: 'oklch(0.68 0.16 25)',      points: [85,82,80,76,74,71,70,68,65,62,60,58] },
    { name: 'Tomoro Coffee',    color: 'oklch(0.62 0.14 145)',     points: [40,42,45,48,52,55,58,56,54,52,50,48] },
    { name: 'Fore Coffee',      color: 'oklch(0.66 0.13 230)',     points: [55,53,50,48,45,43,42,40,38,36,34,32] },
  ],
  weeks: ['W-12','W-11','W-10','W-9','W-8','W-7','W-6','W-5','W-4','W-3','W-2','W-1'],
  mismatches: [
    { brand: 'Kopi Kenanga',     surveyTom: 32.4, trendIdx: 78, verdict: 'aligned',     note: 'Survey TOM 32.4% & search interest 78 — momentum nyata.' },
    { brand: 'Kopi Janji Manis', surveyTom: 28.1, trendIdx: 58, verdict: 'declining',   note: 'Awareness survei masih tinggi tapi search interest turun -27 → kehilangan momentum digital.' },
    { brand: 'Tomoro Coffee',    surveyTom: 18.6, trendIdx: 48, verdict: 'over-indexed', note: 'Search tinggi vs awareness — peluang konversi lewat content.' },
  ],
  relatedQueries: [
    { q: 'kopi kenanga menu',           tag: 'rising',   delta: '+340%' },
    { q: 'kopi kenanga gubeng',         tag: 'breakout', delta: 'breakout' },
    { q: 'kopi kenanga harga',          tag: 'top',      delta: '+120%' },
    { q: 'kopi kenanga delivery',       tag: 'rising',   delta: '+250%' },
    { q: 'kopi kenanga vs janji manis', tag: 'rising',   delta: '+85%' },
    { q: 'kopi kenanga sukolilo',       tag: 'breakout', delta: 'breakout' },
  ],
  geoTopCities: [
    { city: 'Surabaya',  score: 100 },
    { city: 'Sidoarjo',  score: 78 },
    { city: 'Malang',    score: 56 },
    { city: 'Mojokerto', score: 42 },
    { city: 'Gresik',    score: 38 },
    { city: 'Pasuruan',  score: 24 },
  ],
  regionalBreakdown: [
    { region: 'Jawa Timur',     geo: 'ID-JI', scores: [
      { brand: 'Kopi Kenanga',     score: 100, isUs: true },
      { brand: 'Kopi Janji Manis', score: 42 },
      { brand: 'Tomoro Coffee',    score: 30 },
      { brand: 'Fore Coffee',      score: 18 },
    ]},
    { region: 'DKI Jakarta',    geo: 'ID-JK', scores: [
      { brand: 'Kopi Kenanga',     score: 28, isUs: true },
      { brand: 'Kopi Janji Manis', score: 100 },
      { brand: 'Tomoro Coffee',    score: 75 },
      { brand: 'Fore Coffee',      score: 88 },
    ]},
    { region: 'Jawa Tengah',    geo: 'ID-JT', scores: [
      { brand: 'Kopi Kenanga',     score: 45, isUs: true },
      { brand: 'Kopi Janji Manis', score: 60 },
      { brand: 'Tomoro Coffee',    score: 38 },
      { brand: 'Fore Coffee',      score: 30 },
    ]},
    { region: 'Jawa Barat',     geo: 'ID-JB', scores: [
      { brand: 'Kopi Kenanga',     score: 35, isUs: true },
      { brand: 'Kopi Janji Manis', score: 80 },
      { brand: 'Tomoro Coffee',    score: 65 },
      { brand: 'Fore Coffee',      score: 55 },
    ]},
    { region: 'Bali',           geo: 'ID-BA', scores: [
      { brand: 'Kopi Kenanga',     score: 22, isUs: true },
      { brand: 'Kopi Janji Manis', score: 70 },
      { brand: 'Tomoro Coffee',    score: 100 },
      { brand: 'Fore Coffee',      score: 60 },
    ]},
    { region: 'DI Yogyakarta',  geo: 'ID-YO', scores: [
      { brand: 'Kopi Kenanga',     score: 38, isUs: true },
      { brand: 'Kopi Janji Manis', score: 50 },
      { brand: 'Tomoro Coffee',    score: 42 },
      { brand: 'Fore Coffee',      score: 35 },
    ]},
    { region: 'Banten',         geo: 'ID-BT', scores: [
      { brand: 'Kopi Kenanga',     score: 18, isUs: true },
      { brand: 'Kopi Janji Manis', score: 88 },
      { brand: 'Tomoro Coffee',    score: 55 },
      { brand: 'Fore Coffee',      score: 72 },
    ]},
    { region: 'Sumatera Utara', geo: 'ID-SU', scores: [
      { brand: 'Kopi Kenanga',     score: 12, isUs: true },
      { brand: 'Kopi Janji Manis', score: 45 },
      { brand: 'Tomoro Coffee',    score: 28 },
      { brand: 'Fore Coffee',      score: 22 },
    ]},
  ],
};

const RANGE_OPTIONS = [
  { id: '30d', label: '30 hari' },
  { id: '90d', label: '90 hari' },
  { id: '12m', label: '12 bulan' },
];

function TrendsPreview({ campaignId }) {
  // status:
  //   idle        — belum pernah load (lazy: nunggu user klik "Muat")
  //   loading     — first load
  //   refreshing  — manual refresh
  //   live        — punya data live
  //   demo        — SerpAPI key belum di-set, fallback ke demo
  //   error       — fetch failed
  //   quota       — quota habis bulan ini
  const [range, setRange] = React.useState('90d');
  const [state, setState] = React.useState({ status: 'idle', data: null, configured: null, ageMs: 0, error: null, quota: null });

  // Cek configured-status sekali (lightweight) supaya kita tahu mau tampilkan
  // tombol "Muat data" (live) atau langsung demo banner.
  React.useEffect(() => {
    if (!campaignId) return;
    let cancelled = false;
    (async () => {
      try {
        // Cek lewat trends-settings publik? Nggak, itu super-admin only.
        // Solusi: trial fetch tapi backend response cepat kalau cached / not configured.
        // Sebenernya kita memang mau LAZY — jadi cukup tampilkan tombol dulu,
        // dan determine configured saat user klik. Hemat 1 round-trip.
        if (!cancelled) setState({ status: 'idle', data: null, configured: null, ageMs: 0, error: null, quota: null });
      } catch {}
    })();
    return () => { cancelled = true; };
  }, [campaignId]);

  const load = React.useCallback(async (forceFresh, rangeOverride) => {
    if (!campaignId) return;
    const r = rangeOverride || range;
    setState((s) => ({ ...s, status: forceFresh ? 'refreshing' : 'loading' }));
    try {
      const out = forceFresh
        ? await window.api.refreshCampaignTrends(campaignId, r)
        : await window.api.getCampaignTrends(campaignId, r);
      if (out.configured === false) {
        setState({ status: 'demo', data: TRENDS_DEMO, configured: false, ageMs: 0, error: null, quota: null });
        return;
      }
      if (out.quotaExceeded) {
        setState({
          status: out.data ? 'live' : 'quota',
          data: out.data,
          configured: true,
          ageMs: out.ageMs ?? 0,
          error: null,
          quota: out.quota,
          stale: true,
        });
        return;
      }
      if (out.error) {
        setState({ status: 'error', data: TRENDS_DEMO, configured: true, ageMs: 0, error: out.error, quota: null });
        return;
      }
      setState({ status: 'live', data: out.data, configured: true, ageMs: out.ageMs ?? 0, error: null, quota: out.quota ?? null });
    } catch (err) {
      setState({ status: 'error', data: TRENDS_DEMO, configured: false, ageMs: 0, error: err?.message || 'Gagal memuat', quota: null });
    }
  }, [campaignId, range]);

  // Saat user pindah range setelah pernah load, auto-refetch (kalau cached, instan).
  const switchRange = (nextRange) => {
    setRange(nextRange);
    if (state.status !== 'idle') {
      load(false, nextRange);
    }
  };

  const data = state.data || TRENDS_DEMO;
  const isLive = state.status === 'live';
  const isIdle = state.status === 'idle';
  const isQuotaOnly = state.status === 'quota';
  const isDemo = state.status === 'demo' || state.status === 'error';
  const isLoading = state.status === 'loading';
  const isRefreshing = state.status === 'refreshing';

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      {/* Header */}
      <div style={{
        padding: '14px 18px', borderBottom: '1px solid var(--line)',
        display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
      }}>
        <div style={{ flex: 1, minWidth: 240 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>📈 Search Interest (Google Trends)</div>
            {isLive ? (
              <span style={{
                fontSize: 9.5, padding: '3px 8px', borderRadius: 999, fontWeight: 700,
                background: 'oklch(0.96 0.05 155)', color: 'oklch(0.42 0.14 155)',
                letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>{state.stale ? '⚠ Cache (quota habis)' : '● Live data'}</span>
            ) : isIdle ? (
              <span style={{
                fontSize: 9.5, padding: '3px 8px', borderRadius: 999, fontWeight: 700,
                background: 'var(--bg-2)', color: 'var(--ink-3)',
                letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>Belum dimuat</span>
            ) : isQuotaOnly ? (
              <span style={{
                fontSize: 9.5, padding: '3px 8px', borderRadius: 999, fontWeight: 700,
                background: 'oklch(0.96 0.05 25)', color: 'oklch(0.55 0.16 25)',
                letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>Quota habis</span>
            ) : (
              <span style={{
                fontSize: 9.5, padding: '3px 8px', borderRadius: 999, fontWeight: 700,
                background: 'oklch(0.96 0.05 75)', color: 'oklch(0.55 0.16 75)',
                letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>🔬 Preview · demo data</span>
            )}
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>
            {isIdle
              ? <>Klik <b>Muat Data</b> untuk fetch tren search dari Google Trends · cache 24 jam, hemat kuota</>
              : isLive && state.stale
                ? <>Quota SerpAPI bulan ini habis ({state.quota?.used}/{state.quota?.limit}) — menampilkan cache lama dari {ageLabel(state.ageMs)}</>
              : isLive
                ? <>Data live untuk <b>{data.brand}</b> & kompetitor · {data.period} · {ageLabel(state.ageMs)}{state.quota ? ` · ${state.quota.used}/${state.quota.limit} kuota terpakai` : ''}</>
              : isQuotaOnly
                ? <>Quota SerpAPI bulan ini habis ({state.quota?.used}/{state.quota?.limit}) — naikan limit di Super Admin → Integrasi atau tunggu reset bulan depan</>
              : state.configured
                ? <>Tidak bisa fetch ke SerpAPI: <span style={{ color: 'oklch(0.55 0.16 25)' }}>{state.error}</span></>
                : <>Set SerpAPI key di <b>Super Admin → Integrasi</b> untuk aktifkan data live</>
            }
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {/* Range toggle — tampil setelah configured (nggak ngerti gunanya kalau masih demo) */}
          {(isLive || isIdle) && (
            <div style={{
              display: 'inline-flex', borderRadius: 8, overflow: 'hidden',
              border: '1px solid var(--line)', background: 'var(--bg-2)',
            }}>
              {RANGE_OPTIONS.map((opt) => (
                <button
                  key={opt.id}
                  onClick={() => switchRange(opt.id)}
                  style={{
                    padding: '5px 10px', fontSize: 11.5, fontWeight: 600,
                    background: range === opt.id ? 'var(--bg)' : 'transparent',
                    color: range === opt.id ? 'var(--ink)' : 'var(--ink-3)',
                    border: 'none', cursor: 'pointer',
                    borderRight: opt.id !== '12m' ? '1px solid var(--line)' : 'none',
                  }}
                  title={`Tampilkan tren ${opt.label}`}
                >{opt.label}</button>
              ))}
            </div>
          )}

          {isIdle ? (
            <button className="btn btn-primary" onClick={() => load(false)} style={{ padding: '6px 12px', fontSize: 12 }}>
              📥 Muat Data
            </button>
          ) : isLive ? (
            <button
              className="btn btn-ghost"
              onClick={() => load(true)}
              disabled={isRefreshing}
              style={{ padding: '6px 10px', fontSize: 12 }}
            >
              {isRefreshing ? 'Memuat…' : '↻ Refresh'}
            </button>
          ) : null}
        </div>
      </div>

      {/* Idle / quota-empty / loading short-circuits */}
      {isIdle ? (
        <div style={{ padding: '32px 24px', textAlign: 'center' }}>
          <div style={{ fontSize: 36, marginBottom: 8 }}>📊</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 4 }}>
            Lazy-load untuk hemat kuota
          </div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', maxWidth: 460, margin: '0 auto 14px', lineHeight: 1.55 }}>
            Tiap fetch ke Google Trends pakai 3 search dari kuota SerpAPI bulanan kamu. Klik tombol di atas hanya kalau kamu memang butuh lihat data Trends di campaign ini — hasilnya di-cache 24 jam.
          </div>
        </div>
      ) : isQuotaOnly ? (
        <div style={{ padding: '32px 24px', textAlign: 'center' }}>
          <div style={{ fontSize: 36, marginBottom: 8 }}>🔒</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 4 }}>
            Kuota SerpAPI bulan ini habis
          </div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', maxWidth: 460, margin: '0 auto', lineHeight: 1.55 }}>
            Terpakai {state.quota?.used} dari limit {state.quota?.limit}. Reset otomatis tanggal 1 bulan depan, atau upgrade plan SerpAPI dan naikan limit di Super Admin → Integrasi.
          </div>
        </div>
      ) : isLoading ? (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
          Memuat data Google Trends…
        </div>
      ) : (
      <div style={{ padding: 18, display: 'flex', flexDirection: 'column', gap: 18, opacity: isRefreshing ? 0.5 : 1, transition: 'opacity 0.2s' }}>

        <TrendsLineChart data={data} />

        {data.mismatches?.length > 0 && (
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
              Survey TOM vs Search Trends — gap detection
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {data.mismatches.map((m) => {
                const meta = {
                  aligned:        { color: 'oklch(0.42 0.14 155)', bg: 'oklch(0.96 0.05 155)', label: '✅ Aligned' },
                  declining:      { color: 'oklch(0.55 0.16 25)',  bg: 'oklch(0.96 0.05 25)',  label: '📉 Declining digital' },
                  'over-indexed': { color: 'oklch(0.55 0.16 75)',  bg: 'oklch(0.96 0.05 75)',  label: '⚠️ Over-indexed search' },
                  'under-indexed':{ color: 'oklch(0.5 0.13 230)',  bg: 'oklch(0.96 0.05 230)', label: '🔍 Under-indexed digital' },
                }[m.verdict] || { color: 'var(--ink-2)', bg: 'var(--bg-2)', label: m.verdict };
                return (
                  <div key={m.brand} style={{
                    padding: 12, background: meta.bg, borderRadius: 10,
                    display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 12, alignItems: 'flex-start',
                  }}>
                    <div style={{ minWidth: 140 }}>
                      <div style={{ fontSize: 13, fontWeight: 700, color: meta.color }}>{m.brand}</div>
                      <span style={{ fontSize: 10.5, padding: '2px 7px', borderRadius: 999, fontWeight: 700, background: 'rgba(255,255,255,0.6)', color: meta.color, marginTop: 4, display: 'inline-block' }}>{meta.label}</span>
                    </div>
                    <div>
                      <div style={{ display: 'flex', gap: 14, fontSize: 11.5, color: 'var(--ink-2)', marginBottom: 4 }}>
                        <span>Survey TOM: <b className="mono">{m.surveyTom}%</b></span>
                        <span>Trend Idx: <b className="mono">{m.trendIdx}/100</b></span>
                      </div>
                      <div style={{ fontSize: 12, color: 'var(--ink-2)', lineHeight: 1.5 }}>{m.note}</div>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        )}

        <div style={{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: 14 }}>
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
              Related queries (search yang naik)
            </div>
            {data.relatedQueries?.length ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {data.relatedQueries.map((q) => {
                  const tag = {
                    rising:   { color: 'oklch(0.55 0.16 75)',  bg: 'oklch(0.96 0.05 75)' },
                    breakout: { color: 'oklch(0.42 0.14 155)', bg: 'oklch(0.96 0.05 155)' },
                    top:      { color: 'var(--primary)',       bg: 'var(--primary-50)' },
                  }[q.tag] || { color: 'var(--ink-3)', bg: 'var(--bg-2)' };
                  return (
                    <div key={q.q} style={{
                      display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10,
                      alignItems: 'center', padding: '7px 10px',
                      background: 'var(--bg-2)', borderRadius: 8, fontSize: 12.5,
                    }}>
                      <span style={{ color: 'var(--ink-2)' }}>"{q.q}"</span>
                      <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 999, fontWeight: 700, background: tag.bg, color: tag.color, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{q.tag}</span>
                      <span className="mono" style={{ color: tag.color, fontWeight: 700, minWidth: 60, textAlign: 'right' }}>{q.delta}</span>
                    </div>
                  );
                })}
              </div>
            ) : (
              <div style={{ fontSize: 12, color: 'var(--ink-3)', padding: 12, background: 'var(--bg-2)', borderRadius: 8 }}>
                Tidak ada related queries di periode ini.
              </div>
            )}
          </div>

          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10 }}>
              Top kota (search interest)
            </div>
            {data.geoTopCities?.length ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {data.geoTopCities.map((g, i) => (
                  <div key={g.city}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 3 }}>
                      <span style={{ fontWeight: i === 0 ? 700 : 500, color: 'var(--ink-2)' }}>
                        {i === 0 && '🏆 '}{g.city}
                      </span>
                      <span className="mono" style={{ fontWeight: 700, color: 'var(--ink)' }}>{g.score}</span>
                    </div>
                    <div style={{ height: 6, borderRadius: 999, background: 'var(--bg-2)', overflow: 'hidden' }}>
                      <div style={{
                        width: g.score + '%', height: '100%',
                        background: i === 0 ? 'var(--primary)' : 'var(--ink-4)',
                        borderRadius: 999,
                      }} />
                    </div>
                  </div>
                ))}
              </div>
            ) : (
              <div style={{ fontSize: 12, color: 'var(--ink-3)', padding: 12, background: 'var(--bg-2)', borderRadius: 8 }}>
                Tidak ada data geo.
              </div>
            )}
          </div>
        </div>

        {/* ─── Per Wilayah ───────────────────────────────────── */}
        {data.regionalBreakdown?.length > 0 && (
          <RegionalBreakdownSection regions={data.regionalBreakdown} brandName={data.brand} />
        )}

        {isDemo && (
          <div style={{
            padding: '12px 14px', borderRadius: 10,
            background: 'linear-gradient(135deg, var(--primary-50), oklch(0.95 0.06 145))',
            border: '1px solid var(--line)',
            display: 'flex', alignItems: 'flex-start', gap: 10, fontSize: 12.5, lineHeight: 1.55,
          }}>
            <I.Sparkle size={16} style={{ color: 'var(--primary)', flexShrink: 0, marginTop: 2 }} />
            <div>
              <b style={{ color: 'var(--ink)' }}>Aktifkan integrasi untuk data nyata:</b>
              <br/>
              <span style={{ color: 'var(--ink-2)' }}>
                Setelah SerpAPI key di-set, panel ini akan tampilkan tren search asli dari Google Trends untuk brand kamu — termasuk gap detection antara Survey TOM dan search behavior.
              </span>
            </div>
          </div>
        )}
      </div>
      )}
    </div>
  );
}

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

// Catmull-Rom → cubic Bézier untuk smooth curves yang natural-looking.
// Tidak boros memori dan tidak butuh chart library.
function smoothPath(points) {
  if (points.length < 2) return '';
  if (points.length === 2) {
    return `M ${points[0][0]} ${points[0][1]} L ${points[1][0]} ${points[1][1]}`;
  }
  let d = `M ${points[0][0]} ${points[0][1]}`;
  for (let i = 0; i < points.length - 1; i++) {
    const p0 = points[i - 1] ?? points[i];
    const p1 = points[i];
    const p2 = points[i + 1];
    const p3 = points[i + 2] ?? points[i + 1];
    const c1x = p1[0] + (p2[0] - p0[0]) / 6;
    const c1y = p1[1] + (p2[1] - p0[1]) / 6;
    const c2x = p2[0] - (p3[0] - p1[0]) / 6;
    const c2y = p2[1] - (p3[1] - p1[1]) / 6;
    d += ` C ${c1x.toFixed(1)} ${c1y.toFixed(1)}, ${c2x.toFixed(1)} ${c2y.toFixed(1)}, ${p2[0]} ${p2[1]}`;
  }
  return d;
}

function TrendsLineChart({ data }) {
  const W = 760, H = 280;
  const PAD = { top: 18, right: 130, bottom: 32, left: 36 };
  const innerW = W - PAD.left - PAD.right;
  const innerH = H - PAD.top - PAD.bottom;
  const max = 100;
  const series = data.series ?? [];
  const weeks = data.weeks ?? [];

  const svgRef = React.useRef(null);
  const [hoverIdx, setHoverIdx] = React.useState(null);

  if (!series.length || !weeks.length) {
    return (
      <div style={{ fontSize: 12, color: 'var(--ink-3)', padding: 24, textAlign: 'center', background: 'var(--bg-2)', borderRadius: 8 }}>
        Tidak ada data tren untuk periode ini.
      </div>
    );
  }

  const n = weeks.length;
  const xAt = (i) => PAD.left + (i / Math.max(1, n - 1)) * innerW;
  const yAt = (v) => PAD.top + (1 - v / max) * innerH;

  // Brand utama summary
  const us = series.find((s) => s.isUs) || series[0];
  const usPts = us?.points ?? [];
  const usFirst = usPts[0] ?? 0;
  const usLast = usPts[usPts.length - 1] ?? 0;
  const usPeak = usPts.length > 0 ? Math.max(...usPts) : 0;
  const usPeakIdx = usPts.indexOf(usPeak);
  const usAvg = usPts.length > 0 ? Math.round(usPts.reduce((s, v) => s + v, 0) / usPts.length) : 0;
  const growthPct = usFirst > 0 ? Math.round(((usLast - usFirst) / usFirst) * 100) : null;
  const growthColor = growthPct === null ? 'var(--ink-3)'
    : growthPct >= 0 ? 'oklch(0.42 0.14 155)' : 'oklch(0.55 0.16 25)';

  // Sample ~6 x-axis labels evenly
  const labelStride = Math.max(1, Math.ceil(n / 6));

  const onMove = (e) => {
    if (!svgRef.current) return;
    const rect = svgRef.current.getBoundingClientRect();
    const xRel = (e.clientX - rect.left) / rect.width;
    const xVB = xRel * W;
    const i = Math.round(((xVB - PAD.left) / innerW) * (n - 1));
    setHoverIdx(Math.max(0, Math.min(n - 1, i)));
  };

  const usGradId = `tr-grad-us-${(us?.name || 'us').replace(/[^a-z0-9]/gi, '')}`;

  return (
    <div>
      {/* Summary chips */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 8 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
          Tren search interest · {data.period}
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          <SummaryChip label="Puncak" value={usPeak} unit={`(${weeks[usPeakIdx] ? String(weeks[usPeakIdx]).slice(0, 10) : '—'})`} />
          <SummaryChip label="Rata-rata" value={usAvg} />
          <SummaryChip
            label="Pertumbuhan"
            value={growthPct === null ? '—' : `${growthPct >= 0 ? '+' : ''}${growthPct}%`}
            valueColor={growthColor}
            highlight
          />
        </div>
      </div>

      <div className="card" style={{ padding: 14, position: 'relative', background: 'linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%)' }}>
        <svg
          ref={svgRef}
          viewBox={`0 0 ${W} ${H}`}
          style={{ width: '100%', height: 'auto', display: 'block', cursor: 'crosshair' }}
          onMouseMove={onMove}
          onMouseLeave={() => setHoverIdx(null)}
        >
          <defs>
            <linearGradient id={usGradId} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%"   stopColor={us?.color ?? 'var(--primary)'} stopOpacity="0.28" />
              <stop offset="100%" stopColor={us?.color ?? 'var(--primary)'} stopOpacity="0" />
            </linearGradient>
          </defs>

          {/* Subtle grid */}
          {[0, 25, 50, 75, 100].map((v) => (
            <g key={v}>
              <line x1={PAD.left} y1={yAt(v)} x2={W - PAD.right} y2={yAt(v)}
                stroke="var(--line-2)" strokeWidth="0.6" opacity={v === 0 ? 1 : 0.55} />
              <text x={PAD.left - 8} y={yAt(v) + 3.5} fontSize="9.5" fill="var(--ink-3)" textAnchor="end">{v}</text>
            </g>
          ))}

          {/* X labels */}
          {weeks.map((w, i) => (
            i % labelStride === 0 || i === n - 1 ? (
              <text key={i} x={xAt(i)} y={H - 10} fontSize="9.5" fill="var(--ink-3)" textAnchor="middle">
                {String(w).slice(0, 11)}
              </text>
            ) : null
          ))}

          {/* Area fill di bawah brand utama */}
          {us && us.points?.length >= 2 && (() => {
            const pts = us.points.map((v, i) => [xAt(i), yAt(v)]);
            const linePath = smoothPath(pts);
            const areaPath = linePath +
              ` L ${xAt(n - 1).toFixed(1)} ${yAt(0).toFixed(1)}` +
              ` L ${xAt(0).toFixed(1)} ${yAt(0).toFixed(1)} Z`;
            return <path d={areaPath} fill={`url(#${usGradId})`} />;
          })()}

          {/* Lines (kompetitor dulu supaya brand utama di-overlay paling atas) */}
          {[...series].reverse().map((s) => {
            if (!s.points?.length) return null;
            const pts = s.points.map((v, i) => [xAt(i), yAt(v)]);
            const path = smoothPath(pts);
            return (
              <path
                key={s.name}
                d={path}
                fill="none"
                stroke={s.color}
                strokeWidth={s.isUs ? 2.6 : 1.6}
                strokeDasharray={s.isUs ? undefined : '4 4'}
                strokeLinecap="round"
                strokeLinejoin="round"
                opacity={hoverIdx !== null && !s.isUs ? 0.55 : 1}
                style={{ transition: 'opacity 0.15s' }}
              />
            );
          })}

          {/* End-of-line label */}
          {series.map((s) => {
            if (!s.points?.length) return null;
            const last = s.points.length - 1;
            return (
              <g key={`end-${s.name}`}>
                <circle cx={xAt(last)} cy={yAt(s.points[last])} r={s.isUs ? 4.5 : 3.5}
                  fill={s.color} stroke="white" strokeWidth="2" />
                <text x={xAt(last) + 9} y={yAt(s.points[last]) + 3.5} fontSize="10.5"
                  fill={s.color} fontWeight={s.isUs ? 700 : 600}>
                  {s.name.length > 16 ? s.name.slice(0, 15) + '…' : s.name}
                </text>
              </g>
            );
          })}

          {/* Hover crosshair + tooltip */}
          {hoverIdx !== null && (
            <g pointerEvents="none">
              <line x1={xAt(hoverIdx)} y1={PAD.top} x2={xAt(hoverIdx)} y2={H - PAD.bottom}
                stroke="var(--ink-2)" strokeWidth="0.8" strokeDasharray="3 3" opacity="0.5" />
              {series.map((s) => {
                const v = s.points?.[hoverIdx];
                if (v == null) return null;
                return (
                  <circle key={`hover-${s.name}`} cx={xAt(hoverIdx)} cy={yAt(v)} r={s.isUs ? 5 : 4}
                    fill={s.color} stroke="white" strokeWidth="2" />
                );
              })}
              <HoverTooltip
                W={W} H={H} xAt={xAt} hoverIdx={hoverIdx}
                weeks={weeks} series={series}
              />
            </g>
          )}
        </svg>

        {/* Legend */}
        <div style={{
          display: 'flex', flexWrap: 'wrap', gap: 14,
          padding: '12px 4px 0', borderTop: '1px solid var(--line)',
          marginTop: 6, fontSize: 11.5,
        }}>
          {series.map((s) => (
            <div key={s.name} style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              <span style={{
                width: 18, height: 3, borderRadius: 2,
                background: s.color,
                outline: s.isUs ? `1px dashed ${s.color}` : 'none',
                outlineOffset: 2,
              }} />
              <span style={{ color: s.isUs ? 'var(--ink)' : 'var(--ink-2)', fontWeight: s.isUs ? 700 : 500 }}>
                {s.isUs ? '★ ' : ''}{s.name}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function SummaryChip({ label, value, unit, valueColor, highlight }) {
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'baseline', gap: 6,
      padding: '5px 10px', borderRadius: 8,
      background: highlight ? 'oklch(0.97 0.04 155)' : 'var(--bg-2)',
      border: '1px solid ' + (highlight ? 'oklch(0.42 0.14 155 / 0.18)' : 'var(--line-2)'),
      fontSize: 11,
    }}>
      <span style={{ color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.04em', fontWeight: 600 }}>{label}</span>
      <span className="mono" style={{ color: valueColor || 'var(--ink)', fontWeight: 700, fontSize: 12.5 }}>{value}</span>
      {unit && <span style={{ color: 'var(--ink-4)', fontSize: 10 }}>{unit}</span>}
    </div>
  );
}

// Tooltip in-SVG (foreignObject would conflict with the svg ref)
function HoverTooltip({ W, H, xAt, hoverIdx, weeks, series }) {
  const lines = series.map((s) => ({
    name: s.name, color: s.color, isUs: s.isUs,
    val: s.points?.[hoverIdx] ?? 0,
  })).sort((a, b) => b.val - a.val);
  const label = String(weeks[hoverIdx] ?? '').slice(0, 14);

  // Approximate width: 14px per char of longest name + value col, capped 220
  const longest = Math.max(label.length, ...lines.map((l) => l.name.length + 6));
  const boxW = Math.min(220, Math.max(150, longest * 7));
  const boxH = 24 + lines.length * 16;

  // Position: kanan dari crosshair kalau cukup ruang; kiri kalau mepet kanan.
  const cx = xAt(hoverIdx);
  const margin = 14;
  const onRight = cx + margin + boxW < W - 6;
  const x = onRight ? cx + margin : cx - margin - boxW;
  const y = Math.max(8, Math.min(H - boxH - 8, H / 2 - boxH / 2));

  return (
    <g>
      <rect x={x} y={y} width={boxW} height={boxH} rx={8}
        fill="white" stroke="var(--line)" strokeWidth="1"
        style={{ filter: 'drop-shadow(0 4px 12px rgba(0,0,0,0.08))' }} />
      <text x={x + 12} y={y + 16} fontSize="10" fontWeight="700" fill="var(--ink-3)"
        style={{ textTransform: 'uppercase', letterSpacing: '0.04em' }}>
        {label}
      </text>
      {lines.map((l, i) => (
        <g key={l.name}>
          <circle cx={x + 18} cy={y + 32 + i * 16} r="3.5" fill={l.color} />
          <text x={x + 28} y={y + 35 + i * 16} fontSize="10.5"
            fill={l.isUs ? 'var(--ink)' : 'var(--ink-2)'}
            fontWeight={l.isUs ? 700 : 500}>
            {l.name.length > 14 ? l.name.slice(0, 13) + '…' : l.name}
          </text>
          <text x={x + boxW - 12} y={y + 35 + i * 16} fontSize="10.5"
            fill={l.isUs ? 'var(--ink)' : 'var(--ink-2)'}
            fontWeight={700} textAnchor="end" fontFamily="var(--font-mono)">
            {l.val}
          </text>
        </g>
      ))}
    </g>
  );
}

// ─── Per-region brand-vs-competitor comparison ─────────────────────
// 1 SerpAPI call (GEO_MAP) sudah berikan kita data semua wilayah ×
// semua brand. UI ini cuma filter client-side — pilih wilayah mana saja
// yang user mau bandingkan, tanpa extra fetch ke SerpAPI.
function RegionalBreakdownSection({ regions, brandName }) {
  // Auto-pre-select 4 wilayah teratas (by total volume) — sudah di-sort backend.
  const initial = regions.slice(0, 4).map((r) => r.geo);
  const [selected, setSelected] = React.useState(initial);

  React.useEffect(() => {
    setSelected(regions.slice(0, 4).map((r) => r.geo));
  }, [regions]);

  const toggle = (geo) => {
    setSelected((prev) =>
      prev.includes(geo) ? prev.filter((g) => g !== geo) : [...prev, geo]
    );
  };

  const visible = regions.filter((r) => selected.includes(r.geo));

  // Cari max score across semua region terpilih untuk normalisasi bar
  const maxScore = Math.max(
    1,
    ...visible.flatMap((r) => r.scores.map((s) => s.score))
  );

  // Win rate: di berapa wilayah brand kamu #1?
  const winCount = regions.filter((r) => {
    const top = [...r.scores].sort((a, b) => b.score - a.score)[0];
    return top?.isUs;
  }).length;

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 6, flexWrap: 'wrap', gap: 8 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
          Per Wilayah — bandingkan brand vs kompetitor
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
          <b style={{ color: winCount > regions.length / 2 ? 'oklch(0.42 0.14 155)' : 'var(--ink-2)' }}>{brandName}</b> #1 di <b>{winCount}</b>/{regions.length} wilayah
        </div>
      </div>

      {/* Region picker chips */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 12 }}>
        {regions.map((r) => {
          const isOn = selected.includes(r.geo);
          const top = [...r.scores].sort((a, b) => b.score - a.score)[0];
          const usWins = top?.isUs;
          return (
            <button
              key={r.geo}
              onClick={() => toggle(r.geo)}
              style={{
                padding: '5px 10px',
                fontSize: 11.5,
                fontWeight: 600,
                borderRadius: 999,
                border: `1px solid ${isOn ? 'var(--primary)' : 'var(--line)'}`,
                background: isOn ? 'var(--primary-50)' : 'var(--bg)',
                color: isOn ? 'var(--primary)' : 'var(--ink-2)',
                cursor: 'pointer',
                display: 'inline-flex',
                alignItems: 'center',
                gap: 4,
              }}
              title={isOn ? 'Klik untuk sembunyikan' : 'Klik untuk tampilkan'}
            >
              {usWins ? '🏆 ' : ''}{r.region}
            </button>
          );
        })}
      </div>

      {/* Per-region cards */}
      {visible.length === 0 ? (
        <div style={{ fontSize: 12, color: 'var(--ink-3)', padding: 16, background: 'var(--bg-2)', borderRadius: 8, textAlign: 'center' }}>
          Pilih minimal 1 wilayah untuk bandingkan.
        </div>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 10 }}>
          {visible.map((r) => {
            const sorted = [...r.scores].sort((a, b) => b.score - a.score);
            const usRank = sorted.findIndex((s) => s.isUs) + 1;
            const usScore = sorted.find((s) => s.isUs)?.score ?? 0;
            return (
              <div key={r.geo} style={{
                padding: 12,
                border: '1px solid var(--line)',
                borderRadius: 10,
                background: usRank === 1 ? 'oklch(0.97 0.03 155)' : 'var(--bg)',
              }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink)' }}>{r.region}</div>
                  <div style={{ fontSize: 10.5, padding: '2px 7px', borderRadius: 999, fontWeight: 700,
                    background: usRank === 1 ? 'oklch(0.96 0.05 155)' : usRank === 2 ? 'oklch(0.96 0.05 75)' : 'var(--bg-2)',
                    color: usRank === 1 ? 'oklch(0.42 0.14 155)' : usRank === 2 ? 'oklch(0.55 0.16 75)' : 'var(--ink-3)',
                  }}>
                    #{usRank} · {usScore}
                  </div>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  {sorted.map((s) => {
                    const pct = (s.score / maxScore) * 100;
                    const fill = s.isUs ? 'var(--primary)' : 'var(--ink-4)';
                    return (
                      <div key={s.brand} style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 6, alignItems: 'center' }}>
                        <div>
                          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, marginBottom: 2 }}>
                            <span style={{ fontWeight: s.isUs ? 700 : 500, color: s.isUs ? 'var(--primary)' : 'var(--ink-2)' }}>
                              {s.isUs ? '★ ' : ''}{s.brand.length > 18 ? s.brand.slice(0, 17) + '…' : s.brand}
                            </span>
                            <span className="mono" style={{ fontWeight: 600, color: 'var(--ink-3)' }}>{s.score}</span>
                          </div>
                          <div style={{ height: 5, borderRadius: 999, background: 'var(--bg-2)', overflow: 'hidden' }}>
                            <div style={{ width: pct + '%', height: '100%', background: fill, borderRadius: 999 }} />
                          </div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { TrendsPreview });
