// Tablet — Briefing feed with category chips, infinite scroll, and image fallbacks.
// Replaces the static 3-card briefing strip on the iPad NTP.

// ─── Category chips (sticky-feel row) ─────────────────────────────
const BRIEF_CATEGORIES = [
  { id: 'top',       label: 'Top Stories' },
  { id: 'crypto',    label: 'Crypto' },
  { id: 'tech',      label: 'Tech' },
  { id: 'science',   label: 'Science' },
  { id: 'world',     label: 'World' },
  { id: 'finance',   label: 'Finance' },
  { id: 'sports',    label: 'Sports' },
  { id: 'entertain', label: 'Entertainment' },
  { id: 'gaming',    label: 'Gaming' },
  { id: 'diy',       label: 'DIY' },
];

// Source brand color — used both as accent and as the fallback color block.
// Keys are case-insensitive; default fallback is gray.
const BRIEF_SOURCE_COLORS = {
  'al jazeera': '#FBBF24',
  'france 24':  '#22D27A',
  'bbc':        '#6EE7B7',
  'rt':         '#A78BFA',
  'reuters':    '#F472B6',
  'decrypt':    '#F87171',
  'coindesk':   '#60A5FA',
  'the block':  '#22D3EE',
  'cnn':        '#F87171',
  'guardian':   '#60A5FA',
  'nyt':        '#9CA3AF',
  'ft':         '#F9A8D4',
  'verge':      '#C4B5FD',
};
const briefColor = (src) => BRIEF_SOURCE_COLORS[String(src || '').toLowerCase()] || '#9CA3AF';

// Mock generator — produces deterministic stories per category. A real client
// would call its RSS aggregator here. We seed a few image-less stories so the
// fallback design is visible (~30% of cards).
const BRIEF_TEMPLATES = {
  top: [
    { src: 'AL JAZEERA', time: '17m', title: 'Man Utd appoint Michael Carrick as permanent manager' },
    { src: 'FRANCE 24',  time: '40m', title: "Ex-reality TV star Spencer Pratt is 'running Trump's playbook' in LA mayoral race", img: 'https://images.unsplash.com/photo-1495020689067-958852a7765e?w=600' },
    { src: 'RT',         time: '1h',  title: 'How has Latin America responded to the US crackdown on Cuba?', img: 'https://images.unsplash.com/photo-1526778548025-fa2f459cd5c1?w=600' },
    { src: 'BBC',        time: '4m',  title: 'Jury discharged in Ian Watkins murder trial after 14 weeks' },
    { src: 'AL JAZEERA', time: '23m', title: "Hundreds protest Ireland's 'George Floyd moment'" },
    { src: 'FRANCE 24',  time: '44m', title: 'Paris: Far-right blamed as football fans riot in 8th arrondissement', img: 'https://images.unsplash.com/photo-1551038247-3d9af20df552?w=600' },
    { src: 'GUARDIAN',   time: '2h',  title: 'Climate summit ends with watered-down emissions pledge' },
    { src: 'REUTERS',    time: '3h',  title: 'EU drafts mandate to disclose all AI-training data sources by 2027', img: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=600' },
    { src: 'NYT',        time: '5h',  title: 'Inside the small-town factory rebuilding America\'s rare-earth supply' },
  ],
  crypto: [
    { src: 'DECRYPT',  time: '2h',  title: 'OpenAI poaching Coinbase marketing leadership',                     img: 'https://images.unsplash.com/photo-1639322537228-f710d846310a?w=600' },
    { src: 'COINDESK', time: '13h', title: 'Kalshi fines meme-coin candidate for betting on his own election', img: 'https://images.unsplash.com/photo-1605792657660-596af9009e82?w=600' },
    { src: 'THE BLOCK',time: '18h', title: 'Solana mobile shipments cross 100k units worldwide',                img: 'https://images.unsplash.com/photo-1620321023374-d1a68fbc720d?w=600' },
    { src: 'DECRYPT',  time: '1d',  title: 'A long, sober look at the 2026 ETF inflow numbers' },
    { src: 'COINDESK', time: '1d',  title: 'Bitcoin mining hashrate hits new all-time high after Texas freeze' },
    { src: 'THE BLOCK',time: '2d',  title: 'Stablecoin float crosses $300B as remittance corridors mature', img: 'https://images.unsplash.com/photo-1641932969402-2906f37c6552?w=600' },
  ],
  tech: [
    { src: 'VERGE',    time: '1h',  title: 'Apple\'s iPad Pro M5 review: the real generational leap', img: 'https://images.unsplash.com/photo-1611532736597-de2d4265fba3?w=600' },
    { src: 'REUTERS',  time: '4h',  title: 'Nvidia GB300 supply tightens as US-China export rules harden' },
    { src: 'BBC',      time: '6h',  title: 'Researchers show diffusion models can leak training images', img: 'https://images.unsplash.com/photo-1620712943543-bcc4688e7485?w=600' },
    { src: 'FT',       time: '8h',  title: 'Samsung shutters last LCD line, all-OLED roadmap confirmed' },
  ],
  science: [
    { src: 'BBC',      time: '3h',  title: 'JWST captures first direct image of a Saturn-mass exoplanet', img: 'https://images.unsplash.com/photo-1462331940025-496dfbfc7564?w=600' },
    { src: 'GUARDIAN', time: '5h',  title: 'Mediterranean coral cover up 8% after marine-protection rollback' },
    { src: 'REUTERS',  time: '12h', title: 'mRNA trial against pancreatic cancer enters Phase 3' },
  ],
  world: [
    { src: 'AL JAZEERA', time: '1h',  title: 'Sudan ceasefire collapses again, aid corridors closed' },
    { src: 'FRANCE 24',  time: '2h',  title: 'Argentina inflation falls below 1% monthly for the first time in 5 years', img: 'https://images.unsplash.com/photo-1589909202802-8f4aadce1849?w=600' },
    { src: 'RT',         time: '6h',  title: 'Indonesia\'s capital relocation enters its second phase' },
  ],
  finance: [
    { src: 'FT',       time: '40m', title: 'BoJ holds rates as wage data finally turns positive',         img: 'https://images.unsplash.com/photo-1612831661884-1d9a2c41b7d6?w=600' },
    { src: 'REUTERS',  time: '2h',  title: 'Saudi PIF\'s $40B AI buyout fund quietly reopens to LPs' },
    { src: 'NYT',      time: '4h',  title: 'Indian rupee strengthens past 81 on Fed dovish-pivot bets' },
  ],
  sports: [
    { src: 'BBC',      time: '20m', title: 'India clinch ODI series 4-1 in Ahmedabad thriller',          img: 'https://images.unsplash.com/photo-1531415074968-036ba1b575da?w=600' },
    { src: 'GUARDIAN', time: '1h',  title: 'Madrid derby ends 2-2; both sides drop in title race' },
  ],
  entertain: [
    { src: 'FRANCE 24',time: '3h',  title: 'Cannes adds an "AI-assisted" category — and a backlash arrives same day', img: 'https://images.unsplash.com/photo-1485846234645-a62644f84728?w=600' },
    { src: 'BBC',      time: '5h',  title: 'Adele announces 18-month Vegas residency extension' },
  ],
  gaming: [
    { src: 'VERGE',    time: '1h',  title: 'GTA VI launch window confirmed — and it\'s not what you think', img: 'https://images.unsplash.com/photo-1542751371-adc38448a05e?w=600' },
    { src: 'THE BLOCK',time: '4h',  title: 'On-chain gaming finally finds a hit: 1.2M DAU on Soulchain' },
  ],
  diy: [
    { src: 'GUARDIAN', time: '6h',  title: 'A weekend guide to building cedar planters that actually last' },
    { src: 'NYT',      time: '1d',  title: 'How to spec a heat-pump installer (and the questions they hate)' },
  ],
};

// Build a page of stories. We just cycle the templates and re-stamp the times
// to fake an "older as you scroll" feel.
function generateBriefPage(catId, pageIdx) {
  const tpl = BRIEF_TEMPLATES[catId] || BRIEF_TEMPLATES.top;
  const out = [];
  for (let i = 0; i < 9; i++) {
    const t = tpl[(pageIdx * 9 + i) % tpl.length];
    out.push({
      id: `${catId}-${pageIdx}-${i}`,
      src: t.src,
      time: pageIdx === 0 ? t.time : `${pageIdx + 1}d`,
      title: t.title,
      img: t.img,
    });
  }
  return out;
}

// ─── Image-fallback artwork ──────────────────────────────────
// Used whenever a card has no `img`, or the image fails to load.
// We compose source-tinted typography on a noise-textured surface so it
// reads as "intentional editorial card" rather than "missing image" warning.
function BriefImageFallback({ src, title, height = 160, compact = false }) {
  const c = briefColor(src);
  // Pick the largest word for the typographic block (looks nicer than the first).
  const initialChar = (src || '?').trim()[0]?.toUpperCase() || '?';
  return (
    <div style={{
      width: '100%', height,
      borderRadius: 10, overflow: 'hidden', position: 'relative',
      background: c + '1A',
    }}>
      {/* dotted texture */}
      <svg width="100%" height="100%" style={{ position: 'absolute', inset: 0, opacity: 0.5 }} preserveAspectRatio="xMidYMid slice">
        <defs>
          <pattern id={`brief-dots-${c.replace('#','')}`} width="14" height="14" patternUnits="userSpaceOnUse">
            <circle cx="1.5" cy="1.5" r="1" fill={c} opacity="0.18"/>
          </pattern>
        </defs>
        <rect width="100%" height="100%" fill={`url(#brief-dots-${c.replace('#','')})`}/>
      </svg>
      {/* Diagonal accent stripes */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(135deg, ' + c + '22 0%, transparent 50%, ' + c + '12 100%)',
      }}/>
      {/* Source mark */}
      <div style={{
        position: 'absolute', top: compact ? 10 : 14, left: compact ? 10 : 14,
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: '3px 9px', borderRadius: 999,
        background: c, color: '#0B0C0F',
        fontSize: compact ? 9 : 10, fontWeight: 700,
        fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.08em',
      }}>
        <span style={{ width: 5, height: 5, borderRadius: 3, background: '#0B0C0F' }}/>
        {String(src || '').toUpperCase()}
      </div>
      {/* Big initial letter as decoration */}
      <div style={{
        position: 'absolute', right: -10, bottom: -22,
        fontFamily: "'Instrument Serif', Georgia, serif",
        fontSize: compact ? 110 : 160, fontWeight: 400, fontStyle: 'italic',
        color: c, opacity: 0.22, lineHeight: 1,
        pointerEvents: 'none', userSelect: 'none',
      }}>{initialChar}</div>
      {/* Small dotted note — reads as "text story" */}
      <div style={{
        position: 'absolute', bottom: compact ? 10 : 14, left: compact ? 10 : 14, right: compact ? 50 : 70,
      }}>
        <div style={{ height: 2, background: c, opacity: 0.55, borderRadius: 1, marginBottom: 5, width: '90%' }}/>
        <div style={{ height: 2, background: c, opacity: 0.35, borderRadius: 1, marginBottom: 5, width: '75%' }}/>
        <div style={{ height: 2, background: c, opacity: 0.25, borderRadius: 1, width: '55%' }}/>
      </div>
    </div>
  );
}

// Image with fallback — handles both missing src AND failed loads
function BriefImage({ src, alt, sourceName, height = 160, compact = false }) {
  const [failed, setFailed] = React.useState(false);
  if (!src || failed) {
    return <BriefImageFallback src={sourceName} title={alt} height={height} compact={compact}/>;
  }
  return (
    <div style={{
      width: '100%', height, borderRadius: 10, overflow: 'hidden',
      background: M_SURFACE_2, position: 'relative',
    }}>
      <img
        src={src} alt={alt || ''} loading="lazy"
        onError={() => setFailed(true)}
        style={{
          width: '100%', height: '100%', objectFit: 'cover',
          display: 'block',
        }}
      />
    </div>
  );
}

// ─── Briefing card ─────────────────────────────────────────────
function BriefCard({ story }) {
  const c = briefColor(story.src);
  return (
    <div style={{
      background: M_SURFACE, border: '1px solid ' + M_BORDER_SOFT,
      borderRadius: 14, overflow: 'hidden', cursor: 'pointer',
      display: 'flex', flexDirection: 'column',
      transition: 'border 160ms, transform 160ms',
    }}
    onMouseEnter={(e) => { e.currentTarget.style.borderColor = M_BORDER; e.currentTarget.style.transform = 'translateY(-1px)'; }}
    onMouseLeave={(e) => { e.currentTarget.style.borderColor = M_BORDER_SOFT; e.currentTarget.style.transform = 'translateY(0)'; }}
    >
      <BriefImage src={story.img} alt={story.title} sourceName={story.src} height={160}/>
      <div style={{ padding: '14px 16px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
          <span style={{
            display: 'inline-flex', alignItems: 'center', gap: 5,
            padding: '2px 8px', borderRadius: 999,
            background: c + '22', color: c,
            fontSize: 10, fontFamily: 'JetBrains Mono, monospace',
            fontWeight: 700, letterSpacing: '0.08em',
          }}>{story.src}</span>
          <span style={{ fontSize: 10.5, color: M_FG_DIM, fontFamily: 'JetBrains Mono, monospace' }}>{story.time}</span>
        </div>
        <div style={{
          fontSize: 14, fontWeight: 500, lineHeight: 1.4, color: M_FG,
          letterSpacing: '-0.005em',
          textWrap: 'pretty',
        }}>{story.title}</div>
      </div>
    </div>
  );
}

// ─── Briefing feed — sticky category chips + infinite scroll ────
function TBriefingFeed({ landscape = true }) {
  const [activeCat, setActiveCat] = React.useState('top');
  const [pages, setPages] = React.useState(() => [generateBriefPage('top', 0)]);
  const [loading, setLoading] = React.useState(false);
  const [reachedEnd, setReachedEnd] = React.useState(false);
  const sentinelRef = React.useRef(null);

  // Reset when category changes
  React.useEffect(() => {
    setPages([generateBriefPage(activeCat, 0)]);
    setReachedEnd(false);
  }, [activeCat]);

  // IntersectionObserver loads the next page when the sentinel scrolls into view
  React.useEffect(() => {
    if (reachedEnd) return;
    const el = sentinelRef.current;
    if (!el) return;
    let alive = true;
    const io = new IntersectionObserver((entries) => {
      if (!entries[0].isIntersecting || loading) return;
      setLoading(true);
      // Simulate network — real client would await the RSS query
      setTimeout(() => {
        if (!alive) return;
        setPages(p => {
          const next = generateBriefPage(activeCat, p.length);
          if (p.length >= 5) setReachedEnd(true); // cap fake feed
          return [...p, next];
        });
        setLoading(false);
      }, 480);
    }, { rootMargin: '300px 0px' });
    io.observe(el);
    return () => { alive = false; io.disconnect(); };
  }, [activeCat, pages.length, loading, reachedEnd]);

  const flat = pages.flat();
  const cols = landscape ? 3 : 2;

  return (
    <div style={{ marginTop: 8 }}>
      {/* Sticky category chip strip */}
      <div style={{
        position: 'sticky', top: -8, zIndex: 4,
        margin: '0 -8px',
        padding: '14px 8px 12px',
        background: 'linear-gradient(to bottom, ' + M_BG + ' 78%, transparent)',
      }}>
        <div style={{
          display: 'flex', gap: 8, overflowX: 'auto', overflowY: 'hidden',
          scrollbarWidth: 'none', paddingBottom: 2,
        }}>
          {BRIEF_CATEGORIES.map(cat => {
            const active = cat.id === activeCat;
            return (
              <button key={cat.id} onClick={() => setActiveCat(cat.id)} style={{
                padding: '7px 14px', borderRadius: 999,
                background: active ? M_FG : M_SURFACE,
                color: active ? '#0B0C0F' : M_FG_MUTED,
                border: '1px solid ' + (active ? M_FG : M_BORDER_SOFT),
                fontSize: 12.5, fontWeight: active ? 600 : 500, fontFamily: 'inherit',
                cursor: 'pointer', flexShrink: 0, whiteSpace: 'nowrap',
                transition: 'background 160ms, color 160ms, border 160ms',
              }}>{cat.label}</button>
            );
          })}
        </div>
      </div>

      {/* Cards grid */}
      <div style={{
        display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`,
        gap: 14, marginTop: 4,
      }}>
        {flat.map(story => <BriefCard key={story.id} story={story}/>)}
      </div>

      {/* Sentinel + state row */}
      <div ref={sentinelRef} style={{
        padding: '28px 0 12px', textAlign: 'center',
        fontSize: 11, color: M_FG_DIM, fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.08em',
      }}>
        {loading && <BriefLoadingRow cols={cols}/>}
        {!loading && reachedEnd && <span>— END OF FEED —</span>}
        {!loading && !reachedEnd && <span style={{ opacity: 0.5 }}>scroll for more…</span>}
      </div>
    </div>
  );
}

// Skeleton row shown while the next page loads
function BriefLoadingRow({ cols = 3 }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`,
      gap: 14, marginTop: 8,
    }}>
      {Array.from({ length: cols }).map((_, i) => (
        <div key={i} style={{
          background: M_SURFACE, border: '1px solid ' + M_BORDER_SOFT,
          borderRadius: 14, overflow: 'hidden',
        }}>
          <div style={{ height: 160, background: 'linear-gradient(90deg, rgba(255,255,255,0.02), rgba(255,255,255,0.05), rgba(255,255,255,0.02))', backgroundSize: '200% 100%', animation: 'briefShimmer 1.4s linear infinite' }}/>
          <div style={{ padding: 14 }}>
            <div style={{ height: 10, width: 80, borderRadius: 5, background: 'rgba(255,255,255,0.06)', marginBottom: 8 }}/>
            <div style={{ height: 10, width: '92%', borderRadius: 5, background: 'rgba(255,255,255,0.08)', marginBottom: 6 }}/>
            <div style={{ height: 10, width: '70%', borderRadius: 5, background: 'rgba(255,255,255,0.06)' }}/>
          </div>
        </div>
      ))}
      <style>{`@keyframes briefShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }`}</style>
    </div>
  );
}

Object.assign(window, {
  TBriefingFeed, BriefCard, BriefImage, BriefImageFallback,
  BRIEF_CATEGORIES, briefColor, generateBriefPage,
});
