// tnych-logs.jsx — Full analytics dashboard + trap data modal
const { useState, useMemo, useRef, useEffect } = React;

/* ── Helpers ── */
const _fmtDate = (ts) => new Date(ts).toLocaleDateString('en-US', {month:'short', day:'numeric'});
const _fmtDT   = (ts) => new Date(ts).toLocaleString('en-US', {month:'short', day:'numeric', hour:'2-digit', minute:'2-digit'});
const _maskIP  = (ip) => ip || '—';
const _pctChg  = (a, b) => (b == null || b === 0) ? null : Math.round((a - b) / b * 100);

/* ── Icons (logs scope) ── */
const ILClose  = ({s=18}) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
const ILTrend  = ({s=10, up=true}) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{transform: up?'none':'scaleY(-1)'}}><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/></svg>;

/* ── Clicks-over-time chart ── */
function ClicksChart({data}) {
  const [hovIdx, setHovIdx] = useState(null);
  const containerRef = useRef(null);
  const [W, setW] = useState(800);
  const H = 155;
  const P = {t:8, r:4, b:30, l:44};

  useEffect(() => {
    if (!containerRef.current) return;
    const ro = new ResizeObserver(entries => {
      const w = Math.round(entries[0].contentRect.width);
      if (w > 0) setW(w);
    });
    ro.observe(containerRef.current);
    return () => ro.disconnect();
  }, []);
  const pw = W - P.l - P.r, ph = H - P.t - P.b;

  if (!data || data.length < 2) return <div style={{height:160}}/>;

  const maxV    = Math.max(...data.map(d => d.count), 1);
  const niceMax = Math.ceil(maxV / 5) * 5 || 5;
  const xs = (i) => P.l + (i / (data.length - 1)) * pw;
  const ys = (v) => P.t + (1 - v / niceMax) * ph;
  const pts = data.map((d, i) => [xs(i), ys(d.count)]);

  const mkPath = (pts) => pts.reduce((acc, [cx, cy], i) => {
    if (i === 0) return `M${cx},${cy}`;
    const [px, py] = pts[i-1]; const mx = (px + cx) / 2;
    return `${acc} C${mx},${py} ${mx},${cy} ${cx},${cy}`;
  }, '');

  const linePath = mkPath(pts);
  const areaPath = linePath + ` L${pts[pts.length-1][0]},${H-P.b} L${P.l},${H-P.b} Z`;

  const yTicks = [0, 0.5, 1].map(p => ({v: Math.round(niceMax * p), yp: ys(niceMax * p)}));

  const step = Math.max(1, Math.floor(data.length / 5));
  const xLabels = [];
  data.forEach((d, i) => {
    if (i === 0 || i % step === 0 || i === data.length - 1) {
      xLabels.push({
        lbl: new Date(d.date + 'T12:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}),
        xp: xs(i),
      });
    }
  });
  // dedupe x-labels that are too close
  const dedupedXLabels = xLabels.filter((l, i, arr) => i === 0 || l.xp - arr[i-1].xp > 40);

  const onMove = (e) => {
    const r = e.currentTarget.getBoundingClientRect();
    const svgX = ((e.clientX - r.left) / r.width) * W;
    const idx = Math.max(0, Math.min(data.length - 1, Math.round((svgX - P.l) / pw * (data.length - 1))));
    setHovIdx(idx);
  };

  const hov = hovIdx !== null ? data[hovIdx] : null;

  return (
    <div ref={containerRef} style={{position:'relative', width:'100%', userSelect:'none'}}>
      {hov && (
        <div style={{
          position:'absolute', top: (P.t / H * 100) + '%',
          left: (xs(hovIdx) / W * 100) + '%',
          transform:'translateX(-50%) translateY(-115%)',
          background:'var(--surface-2)', border:'1px solid var(--border-hi)',
          borderRadius:8, padding:'5px 10px', fontSize:11,
          pointerEvents:'none', zIndex:10, textAlign:'center',
          whiteSpace:'nowrap', boxShadow:'0 4px 16px oklch(0% 0 0 / 0.35)',
        }}>
          <div style={{fontWeight:700, color:'var(--text)', fontSize:15}}>{hov.count.toLocaleString()}</div>
          <div style={{color:'var(--text-dim)', marginTop:1}}>
            {new Date(hov.date + 'T12:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'})}
          </div>
        </div>
      )}
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" height="160"
        style={{display:'block', overflow:'visible'}}
        onMouseMove={onMove} onMouseLeave={() => setHovIdx(null)}>
        <defs>
          <linearGradient id="lgArea" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   style={{stopColor:'var(--accent)', stopOpacity:0.20}}/>
            <stop offset="100%" style={{stopColor:'var(--accent)', stopOpacity:0.01}}/>
          </linearGradient>
        </defs>

        {/* Y-axis grid */}
        {yTicks.map(({v, yp}) => (
          <g key={v}>
            <line x1={P.l} y1={yp} x2={W-P.r} y2={yp}
              stroke="var(--border)" strokeWidth={v===0?1:0.5} strokeDasharray={v>0?'4 3':''}/>
            <text x={P.l-7} y={yp+4} textAnchor="end" fontSize="10"
              fill="var(--text-dim)" fontFamily="Space Grotesk,sans-serif">
              {v >= 1000 ? `${(v/1000).toFixed(v % 1000 === 0 ? 0 : 1)}k` : v}
            </text>
          </g>
        ))}

        {/* Hover line */}
        {hovIdx !== null && (
          <line x1={xs(hovIdx)} y1={P.t} x2={xs(hovIdx)} y2={H-P.b}
            stroke="var(--border-hi)" strokeWidth="1" strokeDasharray="3 3"/>
        )}

        {/* Area + line */}
        <path d={areaPath} fill="url(#lgArea)"/>
        <path d={linePath} fill="none" stroke="var(--accent)" strokeWidth="2.2"/>

        {/* Hover dot */}
        {hovIdx !== null && (
          <circle cx={xs(hovIdx)} cy={ys(data[hovIdx].count)} r="4.5"
            fill="var(--accent)" stroke="var(--surface)" strokeWidth="2.5"/>
        )}

        {/* X axis */}
        <line x1={P.l} y1={H-P.b} x2={W-P.r} y2={H-P.b} stroke="var(--border)" strokeWidth="1"/>
        {dedupedXLabels.map(({lbl, xp}) => (
          <text key={xp} x={xp} y={H-P.b+16} textAnchor="middle"
            fontSize="9.5" fill="var(--text-dim)" fontFamily="Space Grotesk,sans-serif">{lbl}</text>
        ))}
      </svg>
    </div>
  );
}

/* ── Mini bar chart for breakdowns ── */
function BkdBar({label, count, pct, color='var(--accent)'}) {
  return (
    <div style={{display:'flex', alignItems:'center', gap:10, fontSize:12}}>
      <div style={{width:90, color:'var(--text-muted)', textAlign:'right', flexShrink:0,
        overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{label}</div>
      <div style={{flex:1, height:7, background:'var(--surface-2)', borderRadius:4, overflow:'hidden'}}>
        <div style={{width:`${Math.max(pct,1)}%`, height:'100%', background:color,
          borderRadius:4, transition:'width 0.4s ease'}}/>
      </div>
      <div style={{width:36, color:'var(--text-dim)', textAlign:'right', flexShrink:0,
        fontVariantNumeric:'tabular-nums', fontSize:12, fontWeight:600}}>{pct}%</div>
    </div>
  );
}

function BkdSection({title, items, color}) {
  return (
    <div style={{marginBottom:20}}>
      <div style={{fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.08em',
        color:'var(--text-dim)', marginBottom:10}}>{title}</div>
      <div style={{display:'flex', flexDirection:'column', gap:9}}>
        {items.slice(0,5).map(item => <BkdBar key={item.label} {...item} color={color}/>)}
      </div>
    </div>
  );
}

/* ── LogsView (main analytics page) ── */
function LogsView({isMobile}) {
  const [range, setRange]   = useState('30d');
  const [cStart, setCStart] = useState(() => {
    const d = new Date(); d.setDate(d.getDate() - 14); return d.toISOString().slice(0,10);
  });
  const [cEnd, setCEnd] = useState(() => new Date().toISOString().slice(0,10));
  const [logQ, setLogQ] = useState('');
  const [events, setEvents] = useState([]);

  useEffect(() => {
    fetch('/api/clicks').then(r => r.json()).then(setEvents).catch(e => console.error(e));
  }, []);

  /* ── Filtered event sets ── */
  const {filtered, prevFiltered} = useMemo(() => {
    const now = Date.now();
    let s, e, ps, pe;
    if (range === '30d') {
      e = now; s = now - 30*86400000; pe = s; ps = s - 30*86400000;
    } else if (range === 'all') {
      s = 0; e = now; ps = pe = null;
    } else {
      s  = new Date(cStart + 'T00:00:00').getTime();
      e  = new Date(cEnd   + 'T23:59:59').getTime();
      const dur = e - s; pe = s - 1; ps = s - dur;
    }
    const f  = events.filter(ev => new Date(ev.ts).getTime() >= s && new Date(ev.ts).getTime() <= e);
    const pf = ps != null ? events.filter(ev => new Date(ev.ts).getTime() >= ps && new Date(ev.ts).getTime() <= pe) : null;
    return {filtered: f, prevFiltered: pf};
  }, [range, cStart, cEnd, events]);

  /* ── Daily chart data ── */
  const dailyData = useMemo(() => {
    const now = Date.now();
    let sd, ed;
    if (range === '30d')       { ed = new Date(now); sd = new Date(now - 29*86400000); }
    else if (range === 'all')  { ed = new Date(now); sd = new Date(now - 59*86400000); }
    else { sd = new Date(cStart + 'T00:00:00'); ed = new Date(cEnd + 'T23:59:59'); }

    const cts = {};
    filtered.forEach(ev => { const d = new Date(ev.ts).toISOString().slice(0,10); cts[d] = (cts[d]||0)+1; });

    const res = []; const cur = new Date(sd); cur.setHours(0,0,0,0);
    while (cur <= ed) {
      const k = cur.toISOString().slice(0,10);
      res.push({date: k, count: cts[k]||0});
      cur.setDate(cur.getDate() + 1);
    }
    return res;
  }, [filtered, range, cStart, cEnd]);

  /* ── Summary stats ── */
  const totalClicks = filtered.length;
  const uniqueV     = new Set(filtered.filter(e => e.ip).map(e => e.ip)).size;
  const dayCount    = Math.max(dailyData.length, 1);
  const avgDay      = (totalClicks / dayCount).toFixed(1);
  const prevClicks  = prevFiltered ? prevFiltered.length : null;
  const prevUniq    = prevFiltered ? new Set(prevFiltered.filter(e => e.ip).map(e => e.ip)).size : null;

  const linkCounts = useMemo(() => {
    const c = {}; filtered.forEach(e => { c[e.shortCode] = (c[e.shortCode]||0)+1; }); return c;
  }, [filtered]);

  const topLinkEntry = useMemo(() =>
    Object.entries(linkCounts).sort((a,b) => b[1]-a[1])[0],
    [linkCounts]
  );

  const topLinks = useMemo(() => {
    const tot = filtered.length || 1;
    return Object.entries(linkCounts).sort((a,b) => b[1]-a[1]).slice(0,7)
      .map(([lbl, cnt]) => ({label:`/${lbl}`, count:cnt, pct: Math.round(cnt/tot*100)}));
  }, [linkCounts, filtered.length]);

  /* ── Breakdowns ── */
  const bkd = useMemo(() => {
    const by = (key) => {
      const c = {}; filtered.forEach(e => { const v = e[key]||'Other'; c[v]=(c[v]||0)+1; });
      const tot = filtered.length || 1;
      return Object.entries(c).sort((a,b)=>b[1]-a[1])
        .map(([label,count]) => ({label, count, pct: Math.round(count/tot*100)}));
    };
    return {device:by('device'), browser:by('browser'), os:by('os'), referrer:by('referrer'), country:by('country')};
  }, [filtered]);

  /* ── Table events ── */
  const tableEvents = useMemo(() => {
    const q = logQ.trim().toLowerCase();
    const src = q
      ? filtered.filter(e => e.shortCode.includes(q)||(e.ip||'').includes(q)||(e.referrer||'').toLowerCase().includes(q))
      : filtered;
    return src.slice(0, 50);
  }, [filtered, logQ]);

  const STAT_CARDS = [
    {label:'Total Clicks',   value: totalClicks.toLocaleString(),  change: _pctChg(totalClicks, prevClicks), sub: prevClicks!=null ? `vs ${prevClicks.toLocaleString()} prev period` : null},
    {label:'Unique Visitors',value: uniqueV.toLocaleString(),       change: _pctChg(uniqueV, prevUniq),       sub: prevUniq!=null ? `vs ${prevUniq.toLocaleString()} prev period` : 'tracked IPs only'},
    {label:'Avg / Day',      value: avgDay,                         change: null,                             sub: `over ${dayCount} day${dayCount!==1?'s':''}`},
    {label:'Top Link',       value: topLinkEntry?`/${topLinkEntry[0]}`:'—', change: null,
      sub: topLinkEntry ? `${topLinkEntry[1].toLocaleString()} clicks` : null},
  ];

  return (
    <div>
      {/* Header */}
      <div style={{display:'flex', alignItems:isMobile?'flex-start':'center',
        flexDirection:isMobile?'column':'row', justifyContent:'space-between', marginBottom:20, gap:12}}>
        <div>
          <h1 style={{fontSize:20, fontWeight:700, letterSpacing:'-0.025em', color:'var(--text)', margin:0}}>Analytics</h1>
          <div style={{fontSize:12, color:'var(--text-dim)', marginTop:3}}>
            {totalClicks.toLocaleString()} events &middot; {range==='30d'?'last 30 days':range==='all'?'all time':'custom range'}
          </div>
        </div>
        <div style={{display:'flex', gap:6, alignItems:'center', flexWrap:'wrap'}}>
          {['30d','all','custom'].map(r => (
            <button key={r} onClick={() => setRange(r)}
              className={`btn btn-sm ${range===r?'btn-primary':'btn-ghost'}`}
              style={{padding:'5px 14px'}}>
              {r==='30d'?'30 Days':r==='all'?'All Time':'Custom'}
            </button>
          ))}
        </div>
      </div>

      {/* Custom range pickers */}
      {range === 'custom' && (
        <div style={{display:'flex', gap:8, alignItems:'center', marginBottom:16, flexWrap:'wrap'}}>
          <input type="date" className="input input-sm" value={cStart}
            onChange={e => setCStart(e.target.value)} style={{width:148}}/>
          <span style={{color:'var(--text-dim)', fontSize:13}}>—</span>
          <input type="date" className="input input-sm" value={cEnd}
            onChange={e => setCEnd(e.target.value)} style={{width:148}}/>
        </div>
      )}

      {/* Stat cards */}
      <div style={{display:'grid', gridTemplateColumns:isMobile?'1fr 1fr':'repeat(4,1fr)',
        gap:1, background:'var(--border)', border:'1px solid var(--border)',
        borderRadius:12, overflow:'hidden', marginBottom:20}}>
        {STAT_CARDS.map(sc => (
          <div key={sc.label} style={{background:'var(--surface)', padding:isMobile?'12px 10px':'16px 16px'}}>
            <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:6}}>
              <span style={{fontSize:10, fontWeight:600, textTransform:'uppercase',
                letterSpacing:'0.07em', color:'var(--text-dim)'}}>{sc.label}</span>
              {sc.change != null && (
                <span style={{fontSize:11, fontWeight:700,
                  color:sc.change>=0?'var(--green)':'var(--danger)',
                  display:'flex', alignItems:'center', gap:2}}>
                  <ILTrend s={10} up={sc.change>=0}/>{sc.change>=0?'+':''}{sc.change}%
                </span>
              )}
            </div>
            <div style={{fontSize:isMobile?21:24, fontWeight:700, letterSpacing:'-0.03em',
              color:'var(--text)', lineHeight:1, marginBottom:4, wordBreak:'break-all'}}>{sc.value}</div>
            {sc.sub && <div style={{fontSize:10, color:'var(--text-dim)'}}>{sc.sub}</div>}
          </div>
        ))}
      </div>

      {/* Clicks over time */}
      <div style={{background:'var(--surface)', border:'1px solid var(--border)',
        borderRadius:12, padding:isMobile?'14px 10px':'16px 20px', marginBottom:20}}>
        <div style={{fontSize:11, fontWeight:700, textTransform:'uppercase',
          letterSpacing:'0.08em', color:'var(--text-muted)', marginBottom:14}}>Clicks Over Time</div>
        <ClicksChart data={dailyData}/>
      </div>

      {/* Top Links + Traffic Sources */}
      <div style={{display:'grid', gridTemplateColumns:isMobile?'1fr':'1fr 1fr', gap:16, marginBottom:20}}>

        <div style={{background:'var(--surface)', border:'1px solid var(--border)', borderRadius:12, padding:'16px 18px'}}>
          <div style={{fontSize:11, fontWeight:700, textTransform:'uppercase',
            letterSpacing:'0.08em', color:'var(--text-muted)', marginBottom:12}}>Top Links</div>
          <div style={{display:'flex', flexDirection:'column', gap:7}}>
            {topLinks.map(item => <BkdBar key={item.label} {...item} color="var(--accent)"/>)}
          </div>
        </div>

        <div style={{background:'var(--surface)', border:'1px solid var(--border)',
          borderRadius:12, padding:'16px 18px', overflowY:'auto', maxHeight:460}}>
          <div style={{fontSize:11, fontWeight:700, textTransform:'uppercase',
            letterSpacing:'0.08em', color:'var(--text-muted)', marginBottom:12}}>Traffic Sources</div>
          <BkdSection title="Device"   items={bkd.device}   color="var(--green)"/>
          <BkdSection title="Browser"  items={bkd.browser}  color="var(--purple)"/>
          <BkdSection title="OS"       items={bkd.os}       color="var(--amber)"/>
          <BkdSection title="Referrer" items={bkd.referrer} color="var(--accent)"/>
          <BkdSection title="Country"  items={bkd.country}  color="oklch(64% 0.15 200)"/>
        </div>
      </div>

      {/* Recent events table */}
      <div style={{background:'var(--surface)', border:'1px solid var(--border)',
        borderRadius:12, overflow:'hidden'}}>
        <div style={{padding:'12px 16px 10px', borderBottom:'1px solid var(--border)',
          display:'flex', alignItems:'center', justifyContent:'space-between', gap:10, flexWrap:'wrap'}}>
          <div style={{fontSize:11, fontWeight:700, textTransform:'uppercase',
            letterSpacing:'0.08em', color:'var(--text-muted)'}}>
            Recent Events
            <span style={{color:'var(--text-dim)', fontWeight:400, textTransform:'none',
              letterSpacing:0, fontSize:11, marginLeft:8}}>
              ({filtered.length.toLocaleString()} total)
            </span>
          </div>
          <input className="input input-sm" value={logQ}
            onChange={e => setLogQ(e.target.value)}
            placeholder="Filter by code, IP, referrer…" style={{width:220}}/>
        </div>

        {tableEvents.length === 0 ? (
          <div style={{padding:'32px 20px', textAlign:'center', color:'var(--text-dim)', fontSize:13}}>
            No events match.
          </div>
        ) : (
          <>
            {!isMobile && (
              <div style={{display:'grid',
                gridTemplateColumns:'1fr 0.6fr 1fr 1.5fr 0.75fr 0.85fr 0.55fr 0.42fr',
                gap:'0 10px', padding:'7px 18px',
                borderBottom:'1px solid var(--border)', background:'var(--surface-hi)'}}>
                {['Time','Code','IP','Referrer','Device','Browser','OS','Cty'].map(h => (
                  <span key={h} style={{fontSize:10, fontWeight:700, textTransform:'uppercase',
                    letterSpacing:'0.07em', color:'var(--text-dim)'}}>{h}</span>
                ))}
              </div>
            )}
            <div style={{maxHeight:340, overflowY:'auto'}}>
              {tableEvents.map((ev, i) => (
                <div key={ev.id} style={{
                  display:'grid',
                  gridTemplateColumns: isMobile ? '1fr 1fr' : '1fr 0.6fr 1fr 1.5fr 0.75fr 0.85fr 0.55fr 0.42fr',
                  gap: isMobile?'3px 8px':'0 10px',
                  padding: isMobile?'9px 14px':'7px 18px',
                  borderBottom: i < tableEvents.length-1 ? '1px solid var(--border)' : 'none',
                  fontSize:12, alignItems:'center',
                }}>
                  <span style={{color:'var(--text-dim)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>{_fmtDT(ev.ts)}</span>
                  <code style={{color:'var(--accent)', fontFamily:"'Space Grotesk',monospace", fontWeight:700, fontSize:11}}>/{ev.shortCode}</code>
                  <span style={{color:'var(--text-dim)', fontFamily:'monospace', fontSize:10}}>{_maskIP(ev.ip)}</span>
                  <span style={{color:'var(--text-muted)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{ev.referrer||'—'}</span>
                  <span style={{color:'var(--text-dim)'}}>{ev.device||'—'}</span>
                  <span style={{color:'var(--text-dim)'}}>{ev.browser||'—'}</span>
                  <span style={{color:'var(--text-dim)'}}>{ev.os||'—'}</span>
                  <span style={{color:'var(--text-dim)'}}>{ev.country||'—'}</span>
                </div>
              ))}
            </div>
            {filtered.length > 50 && (
              <div style={{padding:'8px 16px', borderTop:'1px solid var(--border)',
                fontSize:11, color:'var(--text-dim)', textAlign:'center'}}>
                Showing 50 of {filtered.length.toLocaleString()} events
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

/* ── Trap data modal ── */
function TrapDataModal({link, onClose}) {
  const [trapEvents, setTrapEvents] = useState([]);
  useEffect(() => {
    fetch(`/api/clicks/${link.shortCode}`).then(r => r.json()).then(setTrapEvents).catch(e => console.error(e));
  }, [link.shortCode]);

  const events = useMemo(() =>
    trapEvents.sort((a,b) => new Date(b.ts) - new Date(a.ts)),
    [trapEvents]
  );

  const ipCounts = useMemo(() => {
    const c = {}; events.forEach(e => { c[e.ip]=(c[e.ip]||0)+1; }); return c;
  }, [events]);

  const rapidIPs = useMemo(() => {
    const byIP = {};
    events.forEach(e => { if (!byIP[e.ip]) byIP[e.ip]=[]; byIP[e.ip].push(e.ts); });
    const set = new Set();
    Object.entries(byIP).forEach(([ip, times]) => {
      times.sort((a,b)=>a-b);
      for (let i=1;i<times.length;i++) { if (times[i]-times[i-1]<120000){set.add(ip);break;} }
    });
    return set;
  }, [events]);

  const dayData = useMemo(() => {
    const m = {};
    events.forEach(e => { const d=new Date(e.ts).toISOString().slice(0,10); m[d]=(m[d]||0)+1; });
    return Object.entries(m).sort((a,b) => a[0].localeCompare(b[0]));
  }, [events]);
  const maxDay   = Math.max(...dayData.map(([,n])=>n), 1);
  const uniqueIPs = Object.keys(ipCounts).length;
  const firstSeen = events.length ? _fmtDate(events[events.length-1].ts) : '—';
  const lastSeen  = events.length ? _fmtDate(events[0].ts) : '—';
  const repeatClickers = Object.entries(ipCounts).filter(([,n]) => n > 1);

  /* per-code breakdown */
  const by = (key) => {
    const c = {}; events.forEach(e=>{const v=e[key]||'Other';c[v]=(c[v]||0)+1;});
    const tot=events.length||1;
    return Object.entries(c).sort((a,b)=>b[1]-a[1]).map(([label,count])=>({label,count,pct:Math.round(count/tot*100)}));
  };
  const tBkd = {device:by('device'), browser:by('browser'), referrer:by('referrer')};

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal modal-wide" style={{maxWidth:680}} onClick={e=>e.stopPropagation()}>

        {/* Header */}
        <div style={{padding:'16px 20px', borderBottom:'1px solid var(--border)',
          display:'flex', alignItems:'center', justifyContent:'space-between'}}>
          <div>
            <div style={{display:'flex', alignItems:'center', gap:7, marginBottom:3}}>
              <span style={{fontSize:16}}>⚠️</span>
              <span style={{fontSize:15, fontWeight:700, color:'var(--text)'}}>
                Trap Data: <code style={{color:'var(--amber)', fontFamily:"'Space Grotesk',monospace"}}>/{link.shortCode}</code>
              </span>
            </div>
            <div style={{fontSize:12, color:'var(--text-dim)'}}>{link.label}</div>
          </div>
          <button onClick={onClose} style={{background:'none',border:'none',cursor:'pointer',
            color:'var(--text-dim)',display:'flex',padding:4}}>
            <ILClose s={16}/>
          </button>
        </div>

        <div style={{padding:'16px 20px', overflowY:'auto', maxHeight:'calc(90vh - 76px)'}}>
          {events.length === 0 ? (
            <div style={{padding:'32px', textAlign:'center', color:'var(--text-dim)', fontSize:13}}>
              No clicks recorded for this trap yet.
            </div>
          ) : (
            <>
              {/* Stats strip */}
              <div style={{display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:1,
                background:'var(--border)', borderRadius:10, overflow:'hidden', marginBottom:16}}>
                {[
                  {label:'Total Clicks', value:events.length},
                  {label:'Unique IPs',   value:uniqueIPs},
                  {label:'First Seen',   value:firstSeen},
                  {label:'Last Seen',    value:lastSeen},
                ].map(s => (
                  <div key={s.label} style={{background:'var(--surface-hi)', padding:'11px 12px'}}>
                    <div style={{fontSize:9, fontWeight:700, textTransform:'uppercase',
                      letterSpacing:'0.08em', color:'var(--text-dim)', marginBottom:5}}>{s.label}</div>
                    <div style={{fontSize:19, fontWeight:700, color:'var(--text)',
                      letterSpacing:'-0.02em'}}>{s.value}</div>
                  </div>
                ))}
              </div>

              {/* Activity mini chart */}
              {dayData.length > 0 && (
                <div style={{marginBottom:16}}>
                  <div style={{fontSize:10, fontWeight:700, textTransform:'uppercase',
                    letterSpacing:'0.08em', color:'var(--text-dim)', marginBottom:8}}>Click Activity</div>
                  <div style={{display:'flex', alignItems:'flex-end', gap:4, height:44, padding:'0 2px'}}>
                    {dayData.map(([day, n]) => (
                      <div key={day} title={`${day}: ${n} click${n!==1?'s':''}`} style={{
                        flex:1, background:'var(--amber)', borderRadius:'3px 3px 0 0',
                        height:`${(n/maxDay)*100}%`, minHeight:4, opacity:0.85, cursor:'help',
                      }}/>
                    ))}
                  </div>
                  <div style={{display:'flex', justifyContent:'space-between',
                    marginTop:4, fontSize:10, color:'var(--text-dim)'}}>
                    <span>{_fmtDate(new Date(dayData[0][0]+'T12:00:00').getTime())}</span>
                    <span>{_fmtDate(new Date(dayData[dayData.length-1][0]+'T12:00:00').getTime())}</span>
                  </div>
                </div>
              )}

              {/* Breakdown + repeat clickers row */}
              <div style={{display:'grid', gridTemplateColumns: repeatClickers.length>0?'1fr 1fr':'1fr', gap:12, marginBottom:16}}>

                {/* Traffic breakdown */}
                <div style={{background:'var(--surface-hi)', border:'1px solid var(--border)',
                  borderRadius:9, padding:'12px 14px'}}>
                  <div style={{fontSize:10, fontWeight:700, textTransform:'uppercase',
                    letterSpacing:'0.08em', color:'var(--text-dim)', marginBottom:10}}>Traffic Breakdown</div>
                  <BkdSection title="Device"   items={tBkd.device}   color="var(--green)"/>
                  <BkdSection title="Browser"  items={tBkd.browser}  color="var(--purple)"/>
                  <BkdSection title="Referrer" items={tBkd.referrer} color="var(--accent)"/>
                </div>

                {/* Repeat clickers */}
                {repeatClickers.length > 0 && (
                  <div style={{background:'oklch(72% 0.16 52 / 0.08)',
                    border:'1px solid oklch(72% 0.16 52 / 0.22)', borderRadius:9, padding:'12px 14px'}}>
                    <div style={{fontSize:10, fontWeight:700, color:'var(--amber)', marginBottom:10,
                      textTransform:'uppercase', letterSpacing:'0.07em'}}>⚠ Repeat Clickers</div>
                    <div style={{display:'flex', flexDirection:'column', gap:8}}>
                      {repeatClickers.map(([ip, n]) => (
                        <div key={ip} style={{display:'flex', alignItems:'flex-start', gap:7, fontSize:12}}>
                          <div style={{flex:1}}>
                            <code style={{fontFamily:'monospace', color:'var(--text)', fontSize:11}}>{_maskIP(ip)}</code>
                            <div style={{fontSize:10, color:'var(--text-dim)', marginTop:2}}>
                              {n} click{n!==1?'s':''} recorded
                            </div>
                          </div>
                          <div style={{display:'flex', gap:4, flexShrink:0}}>
                            <span className="badge badge-amber">{n}×</span>
                            {rapidIPs.has(ip) && <span className="badge badge-danger">rapid</span>}
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                )}
              </div>

              {/* Click log table */}
              <div style={{fontSize:10, fontWeight:700, textTransform:'uppercase',
                letterSpacing:'0.08em', color:'var(--text-dim)', marginBottom:8}}>Full Click Log</div>
              <div style={{background:'var(--surface-hi)', border:'1px solid var(--border)',
                borderRadius:9, overflow:'hidden'}}>
                <div style={{display:'grid',
                  gridTemplateColumns:'1fr 92px 66px 76px 54px 82px 42px',
                  gap:'0 6px', padding:'6px 12px',
                  background:'var(--surface-2)', borderBottom:'1px solid var(--border)'}}>
                  {['Time','IP','Device','Browser','OS','Referrer','Cty'].map(h => (
                    <span key={h} style={{fontSize:9, fontWeight:700, textTransform:'uppercase',
                      letterSpacing:'0.07em', color:'var(--text-dim)'}}>{h}</span>
                  ))}
                </div>
                <div style={{maxHeight:260, overflowY:'auto'}}>
                  {events.map((ev, i) => {
                    const isRpt = ipCounts[ev.ip] > 1;
                    return (
                      <div key={ev.id} style={{
                        display:'grid',
                        gridTemplateColumns:'1fr 92px 66px 76px 54px 82px 42px',
                        gap:'0 6px', padding:'7px 12px', fontSize:11,
                        borderBottom: i < events.length-1 ? '1px solid var(--border)' : 'none',
                        background: isRpt ? 'oklch(72% 0.16 52 / 0.07)' : 'transparent',
                        alignItems:'center',
                      }}>
                        <span style={{color:'var(--text-dim)', whiteSpace:'nowrap',
                          overflow:'hidden', textOverflow:'ellipsis'}}>{_fmtDT(ev.ts)}</span>
                        <div style={{display:'flex', alignItems:'center', gap:4, overflow:'hidden'}}>
                          <code style={{fontFamily:'monospace',
                            color:isRpt?'var(--amber)':'var(--text-muted)',
                            fontSize:10, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{_maskIP(ev.ip)}</code>
                          {isRpt && <span style={{width:5,height:5,borderRadius:'50%',background:'var(--amber)',flexShrink:0}}/>}
                        </div>
                        <span style={{color:'var(--text-dim)'}}>{ev.device||'—'}</span>
                        <span style={{color:'var(--text-dim)'}}>{ev.browser||'—'}</span>
                        <span style={{color:'var(--text-dim)'}}>{ev.os||'—'}</span>
                        <span style={{color:'var(--text-dim)', overflow:'hidden',
                          textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{ev.referrer||'—'}</span>
                        <span style={{color:'var(--text-dim)'}}>{ev.country||'—'}</span>
                      </div>
                    );
                  })}
                </div>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { LogsView, TrapDataModal });
