// ─── Analytics Module ────────────────────────────────────
function AnalyticsModule(props) {
  var cards = props.cards, cardStats = props.cardStats, showIndex = props.showIndex, MEDIA_TYPES = props.MEDIA_TYPES, db = props.db;
  var [view, setView] = useState('performance');
  var [selectedShow, setSelectedShow] = useState(null);
  var [sortCol, setSortCol] = useState('times_played');
  var [sortDir, setSortDir] = useState('desc');
  var [search, setSearch] = useState('');
  var [isMobile, setIsMobile] = useState(window.innerWidth < 768);
  useEffect(function(){
    function onResize(){setIsMobile(window.innerWidth < 768)}
    window.addEventListener('resize', onResize);
    return function(){window.removeEventListener('resize', onResize)};
  },[]);
  var PAGE_SIZE = 50;
  var [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
  var sentinelRef = useRef(null);

  var cardArray = useMemo(function() {
    if (!cards) return [];
    return Object.values(cards);
  }, [cards]);

  // Merge card data with stats
  var enriched = useMemo(function() {
    return cardArray.map(function(c) {
      var stat = cardStats && cardStats[c.id];
      var shows = stat && stat.shows ? Object.values(stat.shows) : [];
      var timesPlayed = shows.length;
      var avgKnewPct = 0;
      var lastPlayed = null;
      if (shows.length > 0) {
        shows.sort(function(a,b){return (b.date||'').localeCompare(a.date||'')});
        lastPlayed = shows[0].date;
        var totalKnew = shows.reduce(function(s,e){return s + (e.knewPct||0)}, 0);
        avgKnewPct = Math.round(totalKnew / shows.length);
      }
      var cs = crowdScores[c.id];
      return Object.assign({}, c, {
        times_played: timesPlayed,
        avg_knew_pct: avgKnewPct,
        last_played: lastPlayed,
        crowd_score: cs ? cs.crowd_score : null
      });
    });
  }, [cardArray, cardStats]);

  // Sort and filter
  var displayed = useMemo(function() {
    var arr = enriched.slice();
    if (search) {
      var q = search.toLowerCase();
      arr = arr.filter(function(c) {
        return (c.content&&c.content.answer||'').toLowerCase().includes(q) || (c.source||'').toLowerCase().includes(q);
      });
    }
    arr.sort(function(a,b) {
      var av, bv;
      switch(sortCol) {
        case 'answer': av = a.content&&a.content.answer||''; bv = b.content&&b.content.answer||''; return sortDir==='asc'?av.localeCompare(bv):bv.localeCompare(av);
        case 'source': av = a.source||''; bv = b.source||''; return sortDir==='asc'?av.localeCompare(bv):bv.localeCompare(av);
        case 'media_type': av = a.media_type||''; bv = b.media_type||''; return sortDir==='asc'?av.localeCompare(bv):bv.localeCompare(av);
        default: av = a[sortCol]||0; bv = b[sortCol]||0; return sortDir==='asc'?av-bv:bv-av;
      }
    });
    return arr;
  }, [enriched, search, sortCol, sortDir]);

  // Health stats
  var health = useMemo(function() {
    var byMedia = {}; var byDiff = {1:0,2:0,3:0,4:0,5:0}; var byCategory = {}; var byTheme = {};
    var neverPlayed = 0; var lowCrowd = 0; var overplayed = 0;
    enriched.forEach(function(c) {
      byMedia[c.media_type] = (byMedia[c.media_type]||0) + 1;
      byDiff[c.difficulty] = (byDiff[c.difficulty]||0) + 1;
      byCategory[c.category||'Uncategorized'] = (byCategory[c.category||'Uncategorized']||0) + 1;
      (c.themes||[]).forEach(function(t){byTheme[t] = (byTheme[t]||0)+1});
      if (c.times_played === 0) neverPlayed++;
      if (c.crowd_score !== null && c.crowd_score < 3) lowCrowd++;
      if (c.times_played > 5) overplayed++;
    });
    return { byMedia:byMedia, byDiff:byDiff, byCategory:byCategory, byTheme:byTheme, neverPlayed:neverPlayed, lowCrowd:lowCrowd, overplayed:overplayed, total:enriched.length };
  }, [enriched]);

  // Show history
  var shows = useMemo(function() {
    if (!showIndex) return [];
    return Object.entries(showIndex).map(function(e) {
      return Object.assign({id:e[0]}, e[1]);
    }).sort(function(a,b){return (b.created||0)-(a.created||0)});
  }, [showIndex]);

  function toggleSort(col) {
    if (sortCol === col) setSortDir(sortDir==='asc'?'desc':'asc');
    else { setSortCol(col); setSortDir('desc'); }
  }

  // Reset visible count when filters/sort change
  useEffect(function() { setVisibleCount(PAGE_SIZE); }, [search, sortCol, sortDir]);

  // IntersectionObserver for infinite scroll
  useEffect(function() {
    if (!sentinelRef.current) return;
    var obs = new IntersectionObserver(function(entries) {
      if (entries[0].isIntersecting) {
        setVisibleCount(function(prev) { return prev + PAGE_SIZE; });
      }
    }, { rootMargin: '200px' });
    obs.observe(sentinelRef.current);
    return function() { obs.disconnect(); };
  });

  var maxByMedia = Math.max.apply(null, Object.values(health.byMedia).concat([1]));

  if (!cards) return <div className="A-empty"><div className="icon">L</div><div className="msg">LOADING...</div></div>;

  return (
    <div style={{padding:isMobile?12:24,overflow:'auto',height:'100%'}}>
      {/* View tabs */}
      <div style={{display:'flex',gap:8,marginBottom:24}}>
        {['performance','shows','health'].map(function(v) {
          return <button key={v} className={'A-btn' + (view===v?' signal':'')} onClick={function(){setView(v)}}>{v.toUpperCase()}</button>;
        })}
      </div>

      {view === 'performance' && (
        <div>
          {/* KPI cards */}
          <div className="AN-grid">
            <div className="AN-card"><div className="AN-card-val">{health.total}</div><div className="AN-card-label">TOTAL CARDS</div></div>
            <div className="AN-card"><div className="AN-card-val" style={{color:'var(--signal)'}}>{health.neverPlayed}</div><div className="AN-card-label">NEVER PLAYED</div></div>
            <div className="AN-card"><div className="AN-card-val" style={{color:'var(--gold)'}}>{health.overplayed}</div><div className="AN-card-label">OVERPLAYED (5+)</div></div>
            <div className="AN-card"><div className="AN-card-val" style={{color:'var(--cyan)'}}>{shows.length}</div><div className="AN-card-label">TOTAL SHOWS</div></div>
          </div>

          {/* Search */}
          <div style={{marginBottom:16}}>
            <input className="A-input" placeholder="Search cards..." value={search} onChange={function(e){setSearch(e.target.value)}} style={{maxWidth:400}} />
          </div>

          {/* Performance table */}
          <div className={isMobile?'AN-table-wrap':undefined} style={{overflow:'auto'}}>
            <table className="AN-table">
              <thead><tr>
                <th onClick={function(){toggleSort('answer')}}>Answer {sortCol==='answer'?(sortDir==='asc'?'\u2191':'\u2193'):''}</th>
                {!isMobile && <th onClick={function(){toggleSort('source')}}>Source</th>}
                <th onClick={function(){toggleSort('media_type')}}>Type</th>
                <th onClick={function(){toggleSort('scores.lineconic_score')}}>LS</th>
                <th onClick={function(){toggleSort('crowd_score')}}>Crowd</th>
                <th onClick={function(){toggleSort('times_played')}}>Played</th>
                <th onClick={function(){toggleSort('avg_knew_pct')}}>Avg Knew%</th>
                {!isMobile && <th onClick={function(){toggleSort('last_played')}}>Last Played</th>}
              </tr></thead>
              <tbody>
                {displayed.slice(0, visibleCount).map(function(c) {
                  return (
                    <tr key={c.id}>
                      <td style={{maxWidth:isMobile?150:250,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{c.content&&c.content.answer||'—'}</td>
                      {!isMobile && <td style={{maxWidth:150,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',color:'var(--text-sec)'}}>{c.source||'—'}</td>}
                      <td><span style={{fontSize:9,letterSpacing:1,textTransform:'uppercase'}}>{c.media_type}</span></td>
                      <td style={{color:'var(--gold)'}}>{c.scores&&c.scores.lineconic_score||'—'}</td>
                      <td style={{color:c.crowd_score!==null?(c.crowd_score>=5?'#0f8':'var(--signal)'):'var(--text-dim)'}}>{c.crowd_score!==null?c.crowd_score:'—'}</td>
                      <td>{c.times_played}</td>
                      <td>{c.times_played>0?c.avg_knew_pct+'%':'—'}</td>
                      {!isMobile && <td style={{fontSize:10,color:'var(--text-dim)'}}>{c.last_played?new Date(c.last_played).toLocaleDateString():'—'}</td>}
                    </tr>
                  );
                })}
              </tbody>
            </table>
            {visibleCount < displayed.length && <div ref={sentinelRef} style={{height:1}}></div>}
            <div style={{padding:12,fontSize:10,color:'var(--text-dim)',textAlign:'center'}}>Showing {Math.min(visibleCount, displayed.length)} of {displayed.length}</div>
          </div>
        </div>
      )}

      {view === 'shows' && (
        selectedShow
        ? <ShowDetail showId={selectedShow} db={db} cardStats={cardStats} cards={cards} showIndex={showIndex} onBack={function(){setSelectedShow(null)}} />
        : <div>
          <div className="A-section-head">SHOW HISTORY</div>
          {shows.length === 0 && <div style={{color:'var(--text-dim)',padding:24}}>No shows found</div>}
          {shows.map(function(s) {
            return (
              <div key={s.id} onClick={function(){setSelectedShow(s.id)}} style={{padding:'12px 16px',borderBottom:'1px solid var(--border)',display:'flex',justifyContent:'space-between',alignItems:'center',cursor:'pointer'}}>
                <div>
                  <div style={{fontSize:13,color:'#fff'}}>{s.name||s.id}</div>
                  <div style={{fontSize:10,color:'var(--text-sec)',marginTop:2}}>
                    {s.created ? new Date(s.created).toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}) : '—'}
                    {s.duration && ' · '+s.duration}
                    {s.templateId && ' · '+s.templateId}
                  </div>
                </div>
                <div style={{display:'flex',alignItems:'center',gap:8}}>
                  <div style={{fontSize:10,color:'var(--text-dim)'}}>{s.id}</div>
                  <span style={{fontSize:10,color:'var(--cyan)'}}>▶</span>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {view === 'health' && (
        <div>
          {/* Media distribution */}
          <div className="A-section"><div className="A-section-head">MEDIA TYPE DISTRIBUTION</div>
            {Object.entries(health.byMedia).sort(function(a,b){return b[1]-a[1]}).map(function(e) {
              var pct = Math.round(e[1]/health.total*100);
              var colors = {movie:'#fbbf24',music:'#a78bfa',tv:'#60a5fa',internet:'#34d399',ad:'#f472b6',meme:'#fb923c',cliche:'#94a3b8'};
              return <div key={e[0]} style={{marginBottom:8}}>
                <div style={{display:'flex',justifyContent:'space-between',fontSize:10,marginBottom:2}}><span style={{textTransform:'uppercase'}}>{e[0]}</span><span>{e[1]} ({pct}%)</span></div>
                <div className="AN-bar"><div className="AN-bar-fill" style={{width:pct+'%',background:colors[e[0]]||'#555'}}></div></div>
              </div>;
            })}
          </div>

          {/* Difficulty distribution */}
          <div className="A-section"><div className="A-section-head">DIFFICULTY DISTRIBUTION</div>
            {[1,2,3,4,5].map(function(d) {
              var count = health.byDiff[d]||0;
              var pct = health.total>0?Math.round(count/health.total*100):0;
              var colors = ['','#4ade80','#a3e635','#fbbf24','#f97316','#ef4444'];
              return <div key={d} style={{marginBottom:8}}>
                <div style={{display:'flex',justifyContent:'space-between',fontSize:10,marginBottom:2}}><span>DIFFICULTY {d}</span><span>{count} ({pct}%)</span></div>
                <div className="AN-bar"><div className="AN-bar-fill" style={{width:pct+'%',background:colors[d]}}></div></div>
              </div>;
            })}
          </div>

          {/* Category distribution */}
          <div className="A-section"><div className="A-section-head">CATEGORY DISTRIBUTION</div>
            {Object.entries(health.byCategory).sort(function(a,b){return b[1]-a[1]}).map(function(e) {
              var pct = Math.round(e[1]/health.total*100);
              return <div key={e[0]} style={{marginBottom:8}}>
                <div style={{display:'flex',justifyContent:'space-between',fontSize:10,marginBottom:2}}><span>{e[0]}</span><span>{e[1]} ({pct}%)</span></div>
                <div className="AN-bar"><div className="AN-bar-fill" style={{width:pct+'%',background:'var(--cyan)'}}></div></div>
              </div>;
            })}
          </div>

          {/* Flags */}
          <div className="A-section"><div className="A-section-head">CONTENT FLAGS</div>
            <div className="AN-grid">
              <div className="AN-card"><div className="AN-card-val" style={{color:'var(--signal)'}}>{health.neverPlayed}</div><div className="AN-card-label">NEVER PLAYED</div></div>
              <div className="AN-card"><div className="AN-card-val" style={{color:'#ff3333'}}>{health.lowCrowd}</div><div className="AN-card-label">LOW CROWD SCORE (&lt;3)</div></div>
              <div className="AN-card"><div className="AN-card-val" style={{color:'var(--gold)'}}>{health.overplayed}</div><div className="AN-card-label">OVERPLAYED (5+ SHOWS)</div></div>
            </div>
          </div>

          {/* Theme coverage */}
          <div className="A-section"><div className="A-section-head">THEME COVERAGE</div>
            <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
              {Object.entries(health.byTheme).sort(function(a,b){return b[1]-a[1]}).map(function(e) {
                var intensity = Math.min(e[1]/50, 1);
                return <div key={e[0]} style={{padding:'4px 8px',border:'1px solid var(--border)',fontSize:9,textTransform:'uppercase',letterSpacing:'.06em',background:'rgba(0,255,255,'+intensity*0.2+')',color:intensity>0.3?'var(--cyan)':'var(--text-dim)'}}>{e[0]} <b>{e[1]}</b></div>;
              })}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Show Detail ─────────────────────────────────────────
function ShowDetail(props) {
  var showId = props.showId, db = props.db, cardStats = props.cardStats, cards = props.cards, showIndex = props.showIndex, onBack = props.onBack;
  var [showData, setShowData] = useState(null);
  var [loading, setLoading] = useState(true);

  useEffect(function() {
    setLoading(true);
    db.ref('shows/' + showId).once('value').then(function(snap) {
      setShowData(snap.val());
      setLoading(false);
    }).catch(function() { setLoading(false); });
  }, [showId]);

  var indexEntry = showIndex && showIndex[showId];
  var questionTypes = ['source_q','acronym_q','hotseat_prompt','fluency_line','fluency_source'];

  var cardSlides = useMemo(function() {
    if (!showData || !showData.sections) return [];
    var slides = [];
    showData.sections.forEach(function(sec) {
      (sec.slides || []).forEach(function(sl) {
        if (sl.cardId && questionTypes.indexOf(sl.type) >= 0) {
          var knewPct = null;
          if (cardStats && cardStats[sl.cardId] && cardStats[sl.cardId].shows && cardStats[sl.cardId].shows[showId]) {
            knewPct = cardStats[sl.cardId].shows[showId].knewPct;
          }
          var card = cards && cards[sl.cardId];
          slides.push({
            cardId: sl.cardId,
            type: sl.type,
            answer: card && card.content && card.content.answer || sl.content && sl.content.answer || sl.content && sl.content.primary || '—',
            source: card && card.source || sl.content && sl.content.source || '',
            mediaType: card && card.media_type || '',
            difficulty: card && card.difficulty || 0,
            knewPct: knewPct,
            section: sec.name
          });
        }
      });
    });
    return slides;
  }, [showData, cardStats, cards]);

  if (loading) return <div style={{padding:24,color:'var(--text-dim)'}}>LOADING SHOW DATA...</div>;
  if (!showData) return <div style={{padding:24}}><button className="A-btn" onClick={onBack}>← BACK</button><div style={{color:'var(--signal)',marginTop:16}}>Show data not found</div></div>;

  return (
    <div>
      <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}>
        <button className="A-btn" onClick={onBack}>← BACK</button>
        <div>
          <div style={{fontSize:14,color:'#fff',letterSpacing:1}}>{indexEntry && indexEntry.name || showData.name || showId}</div>
          <div style={{fontSize:10,color:'var(--text-sec)',marginTop:2}}>
            {showData.created ? new Date(showData.created).toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}) : ''}
            {showData.duration && ' · ' + showData.duration}
            {showData.templateId && ' · ' + showData.templateId}
          </div>
        </div>
      </div>

      <div className="AN-grid" style={{marginBottom:16}}>
        <div className="AN-card"><div className="AN-card-val">{cardSlides.length}</div><div className="AN-card-label">CARDS USED</div></div>
        <div className="AN-card"><div className="AN-card-val">{showData.sections ? showData.sections.length : 0}</div><div className="AN-card-label">SECTIONS</div></div>
        <div className="AN-card"><div className="AN-card-val">{cardSlides.filter(function(s){return s.knewPct !== null}).length}</div><div className="AN-card-label">WITH STATS</div></div>
      </div>

      <div className="A-section-head">CARD PERFORMANCE</div>
      <div style={{overflow:'auto'}}>
        <table className="AN-table">
          <thead><tr>
            <th>Answer</th>
            <th>Source</th>
            <th>Type</th>
            <th>Diff</th>
            <th>Section</th>
            <th style={{width:160}}>Knew %</th>
          </tr></thead>
          <tbody>
            {cardSlides.map(function(sl, i) {
              var pct = sl.knewPct;
              var barColor = pct === null ? '#333' : pct >= 60 ? '#4ade80' : pct >= 30 ? 'var(--gold)' : 'var(--signal)';
              return (
                <tr key={sl.cardId + '-' + i}>
                  <td style={{maxWidth:200,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{sl.answer}</td>
                  <td style={{maxWidth:120,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',color:'var(--text-sec)'}}>{sl.source}</td>
                  <td><span style={{fontSize:9,letterSpacing:1,textTransform:'uppercase'}}>{sl.type.replace(/_/g,' ')}</span></td>
                  <td>{sl.difficulty || '—'}</td>
                  <td style={{fontSize:10,color:'var(--text-dim)'}}>{sl.section}</td>
                  <td>
                    {pct !== null ? (
                      <div style={{display:'flex',alignItems:'center',gap:6}}>
                        <div style={{flex:1,height:6,background:'#1a1a1a',position:'relative'}}>
                          <div style={{width:Math.min(pct,100)+'%',height:'100%',background:barColor}}></div>
                        </div>
                        <span style={{fontSize:10,color:barColor,minWidth:30,textAlign:'right'}}>{pct}%</span>
                      </div>
                    ) : <span style={{fontSize:10,color:'var(--text-dim)'}}>—</span>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
        {cardSlides.length === 0 && <div style={{padding:24,textAlign:'center',color:'var(--text-dim)',fontSize:12}}>No card slides found in this show</div>}
      </div>
    </div>
  );
}
