/* src/v2/news.jsx — NewsRow: headlines, econ calendar, breadth + bulletin teaser.
   Grid is 6 columns (see .news-row in styles-modules.jsx) — this component
   fills roughly 6 columns with: headlines (2 cols) + calendar (2 cols) +
   earnings-teaser (1 col) + breadth + bulletin (1 col each). */

const { useState: _nwS, useEffect: _nwE } = React;

// Market-news aggregation sources (no user selector — we interleave them all).
const MARKET_NEWS_SOURCES = ['bloomberg', 'cnbc', 'wsj', 'marketwatch', 'yahoo'];

// Legacy chrome rendered titles with innerHTML, which silently decoded HTML
// entities present in some RSS feeds (e.g. WSJ ships smart quotes as `&#x2019;`).
// JSX text nodes don't decode, so we strip the entities here as a safety net —
// the API also decodes server-side, this just covers feeds it might miss.
const _NAMED_ENTITIES = {
  nbsp: ' ', amp: '&', lt: '<', gt: '>', quot: '"', apos: "'",
  lsquo: '‘', rsquo: '’', ldquo: '“', rdquo: '”',
  mdash: '—', ndash: '–', hellip: '…',
};
function decodeText(s) {
  if (!s) return '';
  return String(s)
    .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
    .replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
    .replace(/&([a-zA-Z]+);/g, (m, n) => (n in _NAMED_ENTITIES ? _NAMED_ENTITIES[n] : m));
}

function timeAgo(pubDate) {
  if (!pubDate) return '';
  const then = new Date(pubDate).getTime();
  if (isNaN(then)) return '';
  const diff = Math.floor((Date.now() - then) / 1000);
  if (diff < 60) return diff + 's ago';
  if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
  if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
  return Math.floor(diff / 86400) + 'd ago';
}

// ---------- Market news card (aggregates across major financial desks) ----------
function MarketNewsCard() {
  const [items, setItems] = _nwS([]);
  const [loading, setLoading] = _nwS(true);
  const [err, setErr] = _nwS(null);

  _nwE(() => {
    let alive = true;
    (async () => {
      const results = await Promise.allSettled(
        MARKET_NEWS_SOURCES.map(src =>
          window.TMT_API.getJSON('/rss-news?source=' + src + '&count=6')
        )
      );
      if (!alive) return;
      const merged = results.flatMap(r =>
        r.status === 'fulfilled' ? ((r.value && r.value.items) || []) : []
      );
      merged.sort((a, b) => new Date(b.pubDate || 0) - new Date(a.pubDate || 0));
      // De-dupe identical titles across sources
      const seen = new Set();
      const unique = merged.filter(it => {
        const key = (it.title || '').trim().toLowerCase();
        if (!key || seen.has(key)) return false;
        seen.add(key); return true;
      });
      setItems(unique.slice(0, 20));
      if (unique.length === 0) setErr('Market news unavailable');
      setLoading(false);
    })();
    return () => { alive = false; };
  }, []);

  return (
    <div className="card news-card">
      <div className="card-hd">
        <span className="t">Market news</span>
        <span className="tag live"><span className="d" />live</span>
        <span className="explain">Running feed across Bloomberg, CNBC, WSJ, MarketWatch and Yahoo Finance.</span>
      </div>
      <div className="card-bd">
        {loading && <div style={{ padding: 20, color: 'var(--ink-3)', fontStyle: 'italic', fontFamily: 'var(--serif)' }}>Loading headlines…</div>}
        {!loading && err && items.length === 0 && <div style={{ padding: 20, color: 'var(--ink-3)' }}>{err}</div>}
        {items.map((it, i) => (
          <a key={i} className="news-item" href={it.link} target="_blank" rel="noreferrer" style={{ display: 'block', textDecoration: 'none' }}>
            <div className="meta">
              <span className="src">{it.source || ''}</span>
              <span>·</span>
              <span>{timeAgo(it.pubDate)}</span>
            </div>
            <h5>{decodeText(it.title)}</h5>
          </a>
        ))}
      </div>
    </div>
  );
}

// ---------- Crypto news card ----------
function CryptoNewsCard() {
  const [items, setItems] = _nwS([]);
  const [loading, setLoading] = _nwS(true);
  const [err, setErr] = _nwS(null);

  _nwE(() => {
    let alive = true;
    (async () => {
      try {
        const data = await window.TMT_API.getJSON('/crypto-news?limit=20');
        if (!alive) return;
        const rows = (data && data.data) || [];
        setItems(rows);
        if (rows.length === 0) setErr('Crypto news unavailable');
      } catch (e) {
        if (alive) setErr('Crypto news unavailable');
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, []);

  return (
    <div className="card news-card">
      <div className="card-hd">
        <span className="t">Crypto news</span>
        <span className="tag live"><span className="d" />live</span>
        <span className="explain">Running feed across the major crypto desks — BTC, ETH and broader blockchain.</span>
      </div>
      <div className="card-bd">
        {loading && <div style={{ padding: 20, color: 'var(--ink-3)', fontStyle: 'italic', fontFamily: 'var(--serif)' }}>Loading headlines…</div>}
        {!loading && err && items.length === 0 && <div style={{ padding: 20, color: 'var(--ink-3)' }}>{err}</div>}
        {items.map((it, i) => (
          <a key={i} className="news-item" href={it.url} target="_blank" rel="noreferrer" style={{ display: 'block', textDecoration: 'none' }}>
            <div className="meta">
              <span className="src">{it.source || 'Crypto'}</span>
              <span>·</span>
              <span>{timeAgo(it.published_on)}</span>
            </div>
            <h5>{decodeText(it.title)}</h5>
          </a>
        ))}
      </div>
    </div>
  );
}

// ---------- Econ calendar card ----------
function EconCalendarCard() {
  const [items, setItems] = _nwS(ECON_CAL_SEED);
  const [loading, setLoading] = _nwS(true);

  _nwE(() => {
    let alive = true;
    (async () => {
      try {
        const data = await window.TMT_API.getJSON('/economic-data');
        if (!alive || !data) return;
        // economic-data returns {fred: {...}, bls: {...}} — we display what we have
        // plus our seeded upcoming-releases calendar. Don't remove seed; augment.
        // (Endpoint has thin coverage — seed is the primary source.)
      } catch (e) { /* keep seed */ }
      finally { if (alive) setLoading(false); }
    })();
    return () => { alive = false; };
  }, []);

  return (
    <div className="card news-card" style={{ gridColumn: 'span 2' }}>
      <div className="card-hd">
        <span className="t">This week</span>
        <a className="card-link" href="/calendar.html">See full calendar →</a>
        <span className="explain">Econ releases, Fed speakers, and earnings catalysts in the next 5 sessions.</span>
      </div>
      <div className="card-bd">
        {items.map((it, i) => (
          <div className="cal-item" key={i}>
            <span className="time">{it.time || it.date}</span>
            <span className="t">
              {it.name}
              <span className="d">{it.date} · {it.consensus}</span>
            </span>
            <span className={'imp ' + (it.imp || 'med')}>
              <span /><span /><span />
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------- Breadth card ----------
function BreadthCard() {
  // TODO(breadth-endpoint): no live advance/decline endpoint yet — using
  // a static snapshot based on typical session. Replace when /market-breadth ships.
  const breadth = {
    advancers: 284,
    decliners: 186,
    unchanged: 30,
    newHighs: 54,
    newLows: 12,
    aboveMA50: 54,
    aboveMA200: 61,
    upVolPct: 68,
  };
  const total = breadth.advancers + breadth.decliners + breadth.unchanged;
  const advPct = (breadth.advancers / total) * 100;
  const decPct = (breadth.decliners / total) * 100;
  const adRatio = breadth.decliners > 0 ? (breadth.advancers / breadth.decliners) : null;

  return (
    <div className="card news-card">
      <div className="card-hd">
        <span className="t">Breadth</span>
        <span className="tag cool">S&amp;P 500</span>
        <span className="explain">Advance / decline, new highs vs lows, % above 50-day.</span>
      </div>
      <div className="card-bd breadth">
        <div className="bar">
          <span className="g" style={{ width: advPct + '%' }} />
          <span className="r" style={{ width: decPct + '%' }} />
        </div>
        <div className="legend">
          <span style={{ color: 'var(--up)' }}>▲ {breadth.advancers}</span>
          <span style={{ color: 'var(--ink-3)' }}>● {breadth.unchanged}</span>
          <span style={{ color: 'var(--down)' }}>▼ {breadth.decliners}</span>
        </div>
        <div className="stat"><span className="k">Adv / Dec ratio</span><span className="v">{adRatio != null ? adRatio.toFixed(2) : '—'}</span></div>
        <div className="stat"><span className="k">New highs</span><span className="v" style={{ color: 'var(--up)' }}>{breadth.newHighs}</span></div>
        <div className="stat"><span className="k">New lows</span><span className="v" style={{ color: 'var(--down)' }}>{breadth.newLows}</span></div>
        <div className="stat"><span className="k">Above 50d</span><span className="v">{breadth.aboveMA50}%</span></div>
        <div className="stat"><span className="k">Above 200d</span><span className="v">{breadth.aboveMA200}%</span></div>
        <div className="stat"><span className="k">Up-volume</span><span className="v" style={{ color: breadth.upVolPct >= 50 ? 'var(--up)' : 'var(--down)' }}>{breadth.upVolPct}%</span></div>
      </div>
    </div>
  );
}

// ---------- Bulletin teaser ----------
const BULLETIN_SEED = [
  { tag: 'signal', title: 'Yield curve steepening accelerates',     body: '2s10s at +13bps — most since Aug. Historically equity-positive over 3-month windows.' },
  { tag: 'flow',   title: 'Tech momentum narrows',                   body: 'Breadth inside Nasdaq 100 thinning — semis lead, software lags for third session.' },
  { tag: 'risk',   title: 'HY spreads tightest in 12 months',        body: 'Credit appetite robust; chase-for-yield typical of late-cycle expansion.' },
  { tag: 'watch',  title: 'Core PCE this Friday',                    body: 'Consensus 2.8% YoY — firm print would complicate December cut math.' },
];
function BulletinCard() {
  return (
    <div className="card news-card">
      <div className="card-hd">
        <span className="t">Bulletin</span>
        <a className="card-link" href="/bulletin.html">All briefs →</a>
        <span className="explain">Signals and flow notes from the desk.</span>
      </div>
      <div className="card-bd bul">
        {BULLETIN_SEED.map((it, i) => (
          <div className="bul-item" key={i}>
            <div className="row1">
              <span className={'tag ' + it.tag}>{it.tag}</span>
              <span>just now</span>
            </div>
            <h5>{it.title}</h5>
            <p>{it.body}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------- NewsRow ----------
function NewsRow() {
  return (
    <div className="news-row">
      <MarketNewsCard />
      <CryptoNewsCard />
      <EconCalendarCard />
      <BreadthCard />
      <BulletinCard />
    </div>
  );
}

Object.assign(window, { NewsRow, MarketNewsCard, CryptoNewsCard, EconCalendarCard, BreadthCard, BulletinCard });
