// Main app for NoMoreBSNews.com
// Data is loaded from data/briefings.json via js/data-loader.js
const { useState, useMemo, useEffect, useRef } = React;

// Categories + days are populated after fetch. Until then App renders a loader.
let CATEGORIES = [];
let ALL_DAYS = [];

// Only http(s) URLs are safe to link; everything else (javascript:, data:, etc.) is rejected.
const isHttpUrl = (u) => typeof u === "string" && /^https?:\/\//i.test(u);

const PILLARS = [
  { ttl: "Fact-First", sub: "Verified events" },
  { ttl: "Zero Spin", sub: "No commentary" },
  { ttl: "AI-Curated", sub: "Nightly rebuild" },
  { ttl: "Nonpartisan", sub: "No agenda" },
];

const PillarIcon = ({ name }) => {
  const common = { width: 22, height: 22, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  if (name === "Fact-First") return <svg {...common}><circle cx="11" cy="11" r="6"/><line x1="15.5" y1="15.5" x2="20" y2="20"/></svg>;
  if (name === "Zero Spin") return <svg {...common}><path d="M3 4h18l-7 9v6l-4 1v-7z"/></svg>;
  if (name === "AI-Curated") return <svg {...common}><rect x="5" y="5" width="14" height="14" rx="1"/><rect x="9" y="9" width="6" height="6"/><line x1="2" y1="9" x2="5" y2="9"/><line x1="2" y1="15" x2="5" y2="15"/><line x1="19" y1="9" x2="22" y2="9"/><line x1="19" y1="15" x2="22" y2="15"/><line x1="9" y1="2" x2="9" y2="5"/><line x1="15" y1="2" x2="15" y2="5"/><line x1="9" y1="19" x2="9" y2="22"/><line x1="15" y1="19" x2="15" y2="22"/></svg>;
  if (name === "Nonpartisan") return <svg {...common}><line x1="12" y1="3" x2="12" y2="21"/><path d="M5 8h14l-3 7h-8z"/><line x1="3" y1="20" x2="21" y2="20"/></svg>;
  return null;
};

// ── Live clock for the utility bar ─────────────────────────────────────────
function useClock() {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(t);
  }, []);
  return now;
}

const UtilityBar = ({ activeDay }) => {
  const now = useClock();
  const timeStr = now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
  return (
    <div className="util-bar">
      <div className="util-inner">
        <div>
          <span className="dot"></span>
          {activeDay.id === ALL_DAYS[0].id ? "Live Edition" : "Archive Edition"}
          <span className="meta" style={{ marginLeft: 14 }}>UPDATED 06:00 ET · NEXT BUILD IN {countdownTo6AM(now)}</span>
        </div>
        <div className="meta">
          <span style={{ color: "var(--tan)" }}>UTC {timeStr}</span>
          <span style={{ marginLeft: 14 }}>NYC · LON · TYO</span>
        </div>
      </div>
    </div>
  );
};

function countdownTo6AM(now) {
  const next = new Date(now);
  next.setHours(6, 0, 0, 0);
  if (next <= now) next.setDate(next.getDate() + 1);
  const diff = next - now;
  const h = Math.floor(diff / 3600000);
  const m = Math.floor((diff % 3600000) / 60000);
  const s = Math.floor((diff % 60000) / 1000);
  return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}

// ── Masthead ───────────────────────────────────────────────────────────────
const Masthead = ({ activeDay }) => {
  return (
    <header className="masthead">
      <div className="masthead-inner">
        <div className="masthead-side">
          <span className="line">Edition <span className="value">{activeDay.edition.replace("Vol. 1 · No. ", "№ ")}</span></span>
          <span className="line">Curated <span className="value">{activeDay.generatedAt}</span></span>
          <span className="line">Read time <span className="value">{activeDay.readMinutes} min</span></span>
        </div>
        <div className="masthead-title">
          <h1>NoMoreBSNews<span className="dotcom">.com</span></h1>
          <div className="tagline">
            <span className="sep"></span>
            Top stories · Zero spin
            <span className="sep"></span>
          </div>
        </div>
        <div className="masthead-side right">
          <span className="line"><span className="value">{activeDay.date}</span></span>
          <span className="line">{activeDay.stories.length} stories · {CATEGORIES.length} desks</span>
          <span className="line">Built by AI · Verified by source</span>
        </div>
      </div>
    </header>
  );
};

// ── Day strip ──────────────────────────────────────────────────────────────
const DayStrip = ({ activeId, onPick }) => {
  return (
    <nav className="daystrip" aria-label="Day archive">
      <div className="daystrip-inner">
        <div className="daystrip-label">Archive →</div>
        {ALL_DAYS.map(d => (
          <button
            key={d.id}
            className={"day-tab" + (d.id === activeId ? " active" : "")}
            onClick={() => onPick(d.id)}
          >
            <span className="tab-label">{d.dayLabel}</span>
            <span className="tab-date">{d.shortDate}</span>
          </button>
        ))}
      </div>
    </nav>
  );
};

// ── Brief hero ─────────────────────────────────────────────────────────────
const BriefHero = ({ day }) => {
  const counts = CATEGORIES.reduce((acc, c) => {
    acc[c.id] = day.stories.filter(s => s.cat === c.id).length;
    return acc;
  }, {});
  const topDesks = CATEGORIES.filter(c => counts[c.id] > 0).length;
  return (
    <section className="brief-hero">
      <div className="brief-hero-inner">
        <div className="brief-hero-eyebrow">
          <span className={"stamp " + (day.id === ALL_DAYS[0].id ? "red" : "")}>
            {day.id === ALL_DAYS[0].id ? "Today's Briefing" : "Archive · " + day.dayLabel}
          </span>
          <span className="timestamp">{day.date.toUpperCase()} · BUILD {day.generatedAt}</span>
        </div>
        <h2>At a Glance</h2>
        <p className="summary">{day.summary}</p>
        <div className="brief-stats">
          <div className="brief-stat">
            <div className="val red">{day.stories.length}</div>
            <div className="lbl">Stories</div>
          </div>
          <div className="brief-stat">
            <div className="val">{topDesks}</div>
            <div className="lbl">Desks</div>
          </div>
          <div className="brief-stat">
            <div className="val">{day.readMinutes}<span style={{ fontSize: 14, fontWeight: 500, color: "var(--brown)", marginLeft: 4 }}>min</span></div>
            <div className="lbl">Read time</div>
          </div>
          <div className="brief-stat">
            <div className="val">0</div>
            <div className="lbl">Op-eds · Hot takes · Spin</div>
          </div>
        </div>
      </div>
    </section>
  );
};

// ── Category jump rail ─────────────────────────────────────────────────────
const CategoryRail = ({ day, onJump }) => {
  return (
    <div className="cat-rail">
      <span className="cat-rail-label">Jump to desk</span>
      {CATEGORIES.map(c => {
        const count = day.stories.filter(s => s.cat === c.id).length;
        if (count === 0) return null;
        return (
          <button key={c.id} className="cat-chip" onClick={() => onJump(c.id)}>
            {c.label}<span className="count">{String(count).padStart(2, "0")}</span>
          </button>
        );
      })}
    </div>
  );
};

// ── Story card ─────────────────────────────────────────────────────────────
const Story = ({ story, cat, showSources, showTime }) => {
  return (
    <article className="story" data-comment-anchor={`story-${story.n}`}>
      <div className="story-rank">
        <span className="rank-num">{String(story.n).padStart(2, "0")}</span>
        <span className="rank-meta">№{story.n}</span>
      </div>
      <div className="story-body">
        <div className="story-tags">
          <span className="story-tag red">{cat.tag}</span>
          {showTime && <span className="story-time">{story.time} ET</span>}
        </div>
        <h3>{isHttpUrl(story.url) ? <a href={story.url} target="_blank" rel="noopener noreferrer">{story.headline}</a> : story.headline}</h3>
        <p className="dossier">{story.dossier}</p>
        {showSources && story.sources && (
          <div className="story-sources">
            {story.sources.map((s, i) => (
              <span key={i} className="story-source">{s}</span>
            ))}
          </div>
        )}
      </div>
    </article>
  );
};

// ── Section ────────────────────────────────────────────────────────────────
const Section = ({ cat, stories, showSources, showTime, refSetter }) => {
  if (stories.length === 0) return null;
  return (
    <section data-screen-label={cat.label} ref={refSetter}>
      <div className="section-header">
        <span className="section-tag">{cat.tag}</span>
        <div className="section-title">
          {cat.label}
          <span className="note">— {cat.note}</span>
        </div>
        <span className="section-count">{String(stories.length).padStart(2, "0")} stories</span>
      </div>
      {stories.map(s => <Story key={s.n} story={s} cat={cat} showSources={showSources} showTime={showTime} />)}
    </section>
  );
};

// ── Sidebar ────────────────────────────────────────────────────────────────
const Sidebar = ({ day }) => {
  const counts = CATEGORIES.map(c => ({
    cat: c,
    n: day.stories.filter(s => s.cat === c.id).length,
  }));
  const max = Math.max(...counts.map(c => c.n));
  return (
    <aside className="sidebar">
      {/* Promo for MyAINewsBot */}
      <div className="promo" data-comment-anchor="promo">
        <div className="promo-eyebrow">Want the full picture?</div>
        <h3>The same news, deeper.<br/><span className="accent">MyAINewsBot.com</span></h3>
        <p>This site is the briefing. <b style={{ color: "var(--paper)" }}>MyAINewsBot</b> is the full feed — live updates, personalized topics, source diffing, and your own AI editor.</p>
        <ul className="promo-list">
          <li>Live story tracking across 800+ sources</li>
          <li>Custom desks · saved filters</li>
          <li>Source diffing & claim verification</li>
          <li>Personal AI editor & briefings</li>
        </ul>
        <a href="https://myainewsbot.com" target="_blank" rel="noopener" className="promo-cta">
          Try MyAINewsBot <span className="arrow">→</span>
        </a>
      </div>

      {/* Coverage histogram */}
      <div className="side-card">
        <div className="side-eyebrow">Today's Coverage</div>
        <h3>How the {day.stories.length} stories split</h3>
        <div className="coverage-bars">
          {counts.map(({ cat, n }) => (
            <div key={cat.id} className="coverage-row">
              <span className="label">{cat.label}</span>
              <span className="bar">
                <span className="bar-fill" style={{ width: `${(n / max) * 100}%` }}></span>
              </span>
              <span className="count">{String(n).padStart(2, "0")}</span>
            </div>
          ))}
        </div>
      </div>

      {/* Pillars */}
      <div className="side-card">
        <div className="side-eyebrow">Editorial Standard</div>
        <h3>Four rules. No exceptions.</h3>
        <div className="pillars">
          {PILLARS.map(p => (
            <div key={p.ttl} className="pillar">
              <div className="glyph"><PillarIcon name={p.ttl}/></div>
              <div className="ttl">{p.ttl}</div>
              <div className="sub">{p.sub}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Methodology */}
      <div className="side-card">
        <div className="side-eyebrow">Methodology</div>
        <h3>How tonight's edition was built</h3>
        <div className="methodology">
          <ol>
            <li>Ingest <code>~14K</code> stories across 800+ sources, 18:00–04:00 ET.</li>
            <li>Cluster by event. Drop duplicates, opinion pieces, partisan framing.</li>
            <li>Rank by independent-source count and event significance.</li>
            <li>Re-write each top story as a neutral 2-sentence dossier.</li>
            <li>Cross-verify facts against primary sources. Cite each one.</li>
          </ol>
        </div>
      </div>

      {/* Dark print/share card */}
      <div className="side-card dark">
        <div className="side-eyebrow">Briefing Tools</div>
        <h3 style={{ color: "var(--paper)" }}>Take it with you.</h3>
        <p style={{ fontFamily: "var(--sans)", fontSize: 13, lineHeight: 1.55, color: "#d4c4b3", margin: "0 0 14px" }}>
          The whole edition fits on two pages. Print it for your morning coffee.
        </p>
        <button
          onClick={() => window.print()}
          style={{
            background: "transparent", border: "1px solid var(--tan)", color: "var(--paper)",
            padding: "9px 14px", fontFamily: "var(--sans)", fontWeight: 600, fontSize: 12,
            letterSpacing: "0.05em", cursor: "pointer", textTransform: "uppercase",
          }}
        >
          Print this edition
        </button>
      </div>
    </aside>
  );
};

// ── Footer ─────────────────────────────────────────────────────────────────
const Footer = () => (
  <>
    <footer className="site-footer">
      <div className="footer-inner">
        <div className="footer-col">
          <div style={{ marginBottom: 14 }}>
            <NoBSWordmark size={20} />
          </div>
          <p>An AI-curated daily briefing. 30 stories. Zero spin. Built nightly, read in nine minutes, gone tomorrow.</p>
        </div>
        <div className="footer-col">
          <h4>The Briefing</h4>
          <a href="#today">Today</a>
          <a href="#archive">Past 7 days</a>
          <a href="#print">Print edition</a>
          <a href="#methodology">Methodology</a>
        </div>
        <div className="footer-col">
          <h4>Full Feed</h4>
          <a href="https://myainewsbot.com" target="_blank" rel="noopener">MyAINewsBot.com ↗</a>
          <a href="#desks">Custom desks</a>
          <a href="#alerts">Personal alerts</a>
          <a href="#api">API access</a>
        </div>
        <div className="footer-col">
          <h4>About</h4>
          <a href="#standards">Editorial standards</a>
          <a href="#sources">Source list</a>
          <a href="#contact">Contact</a>
          <a href="#colophon">Colophon</a>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 NoMoreBSNews · Sidecar to MyAINewsBot.com</span>
        <span>Built nightly · Verified at source · No accounts · No tracking</span>
      </div>
    </footer>
  </>
);

// ── Tweaks panel ───────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "density": "comfortable",
  "showSources": true,
  "showTime": true,
  "showPromo": true,
  "accent": "#D62828"
}/*EDITMODE-END*/;

const NoBSTweaks = ({ tweaks, setTweak }) => (
  <TweaksPanel title="Tweaks">
    <TweakSection title="Layout">
      <TweakRadio
        label="Density"
        value={tweaks.density}
        onChange={v => setTweak("density", v)}
        options={[
          { value: "compact", label: "Compact" },
          { value: "comfortable", label: "Default" },
          { value: "roomy", label: "Roomy" },
        ]}
      />
      <TweakToggle label="Show source citations" value={tweaks.showSources} onChange={v => setTweak("showSources", v)} />
      <TweakToggle label="Show timestamps" value={tweaks.showTime} onChange={v => setTweak("showTime", v)} />
      <TweakToggle label="Show MyAINewsBot promo" value={tweaks.showPromo} onChange={v => setTweak("showPromo", v)} />
    </TweakSection>
    <TweakSection title="Brand">
      <TweakColor
        label="Accent color"
        value={tweaks.accent}
        onChange={v => setTweak("accent", v)}
        options={["#D62828", "#3B2A1E", "#7A4B2E", "#1F6FEB"]}
      />
    </TweakSection>
  </TweaksPanel>
);

// ── App ────────────────────────────────────────────────────────────────────
const LoadingScreen = () => (
  <div style={{
    minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center",
    fontFamily: "'JetBrains Mono', monospace", fontSize: 12, letterSpacing: "0.16em",
    textTransform: "uppercase", color: "var(--brown)", background: "var(--paper)",
  }}>
    <span style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
      <span style={{ width: 8, height: 8, background: "var(--red)", borderRadius: "50%",
        animation: "pulse 1.4s ease-out infinite" }}/>
      Loading today's briefing…
    </span>
  </div>
);

const App = () => {
  const [data, setData] = useState(null);
  const [activeId, setActiveId] = useState(null);
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const sectionRefs = useRef({});

  useEffect(() => {
    window.NMBS.loadBriefings().then((d) => {
      CATEGORIES = d.categories;
      ALL_DAYS = d.days;
      setData(d);
      setActiveId(d.days[0].id);
    }).catch(err => {
      console.error("Failed to load briefings:", err);
    });
  }, []);

  // apply accent (kept above the early-return so hook order is stable)
  useEffect(() => {
    document.documentElement.style.setProperty("--red", tweaks.accent);
  }, [tweaks.accent]);

  if (!data || !activeId) return <LoadingScreen />;
  const activeDay = ALL_DAYS.find(d => d.id === activeId) || ALL_DAYS[0];

  const jumpTo = (catId) => {
    const el = sectionRefs.current[catId];
    if (el) {
      const y = el.getBoundingClientRect().top + window.scrollY - 20;
      window.scrollTo({ top: y, behavior: "smooth" });
    }
  };

  return (
    <div className={`density-${tweaks.density}`}>
      <UtilityBar activeDay={activeDay} />
      <Masthead activeDay={activeDay} />
      <DayStrip activeId={activeId} onPick={setActiveId} />
      <main className="shell" data-screen-label={`Briefing ${activeDay.shortDate}`}>
        <div className="layout">
          <div>
            <BriefHero day={activeDay} />
            <CategoryRail day={activeDay} onJump={jumpTo} />
            {CATEGORIES.map(c => {
              const stories = activeDay.stories.filter(s => s.cat === c.id);
              return (
                <Section
                  key={c.id}
                  cat={c}
                  stories={stories}
                  showSources={tweaks.showSources}
                  showTime={tweaks.showTime}
                  refSetter={el => sectionRefs.current[c.id] = el}
                />
              );
            })}
          </div>
          {tweaks.showPromo && <Sidebar day={activeDay} />}
        </div>
      </main>
      <Footer />
      <NoBSTweaks tweaks={tweaks} setTweak={setTweak} />
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
