// News — engagement-ranked stories from /api/news (proxied to project-manager,
// populated every 8h by the automation-runner news-digest job).
const { useState, useEffect } = React;

function newsRelTime(ts) {
  if (!ts) return "";
  const t = typeof ts === "number" ? ts : Date.parse(ts);
  if (!t || isNaN(t)) return "";
  const s = Math.max(0, (Date.now() - t) / 1000);
  if (s < 60) return "just now";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  if (s < 86400) return Math.floor(s / 3600) + "h ago";
  if (s < 172800) return "yesterday";
  return Math.floor(s / 86400) + "d ago";
}

function newsEngagement(engagement) {
  return Object.entries(engagement || {})
    .filter(([, v]) => Number.isFinite(Number(v)) && Number(v) > 0)
    .slice(0, 3)
    .map(([k, v]) => `${Number(v).toLocaleString()} ${k.replace(/^num_/, "").replace(/_/g, " ")}`)
    .join(" · ");
}

function NewsRow({ item }) {
  const url = /^https?:\/\//i.test(item.url || "") ? item.url : "";
  const meta = [newsRelTime(item.publishedAt), newsEngagement(item.engagement)].filter(Boolean).join(" · ");
  return (
    <a className="row" href={url || undefined} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "inherit" }}>
      <div className="row-main">
        <b>{item.title}</b>
        {item.summary && <div className="row-sub">{item.summary}</div>}
      </div>
      <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 }}>
        {item.source && <span className="badge pri">{item.source}</span>}
        {meta && <span className="faint mono" style={{ fontSize: 11.5 }}>{meta}</span>}
      </div>
    </a>
  );
}

// Topic subscriptions editor — reads/writes /api/news/topics. The automation
// runner watches the topics file and starts a fresh scrape within a minute of
// a save; new results land in the feed once the scrape finishes.
function TopicsEditor({ toast }) {
  const [draft, setDraft] = useState(null);   // null while loading
  const [input, setInput] = useState("");
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    let alive = true;
    fetch("/api/news/topics", { headers: { "content-type": "application/json" } })
      .then((res) => (res.ok ? res.json() : Promise.reject(new Error(String(res.status)))))
      .then((body) => { if (alive) setDraft(Array.isArray(body?.topics) ? body.topics : []); })
      .catch(() => { if (alive) setDraft([]); });
    return () => { alive = false; };
  }, []);

  const add = () => {
    const topic = input.trim();
    if (!topic || !draft) return;
    if (draft.some((t) => t.toLowerCase() === topic.toLowerCase())) { setInput(""); return; }
    setDraft([...draft, topic]);
    setInput("");
  };
  const remove = (topic) => setDraft((prev) => prev.filter((t) => t !== topic));

  const save = async () => {
    setSaving(true);
    try {
      const res = await fetch("/api/news/topics", {
        method: "PUT",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ topics: draft }),
      });
      const body = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(body.error || `Request failed: ${res.status}`);
      setDraft(body.topics || draft);
      toast("Topics saved · a fresh scrape starts within a minute");
    } catch (e) { toast(e.message); }
    setSaving(false);
  };

  return (
    <div className="card" style={{ marginBottom: "var(--gap)" }}>
      <div className="card-head">
        <div>
          <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Subscribed topics</h2>
          <p className="mono" style={{ fontSize: 12 }}>each topic becomes a tab · saving re-scrapes within a minute (results take a few minutes per topic)</p>
        </div>
      </div>
      {draft === null && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>Loading topics…</div>}
      {draft !== null && (
        <>
          <div className="chips" style={{ marginBottom: 12 }}>
            {draft.map((topic) => (
              <span className="tag" key={topic} style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
                {topic}
                <button className="btn btn-subtle btn-sm" style={{ padding: 2, minHeight: 0, height: "auto", border: "none" }} onClick={() => remove(topic)} title={`Remove ${topic}`} aria-label={`Remove ${topic}`}>
                  <Icon name="x" size={12} />
                </button>
              </span>
            ))}
            {!draft.length && <span className="faint" style={{ fontSize: 12 }}>No topics yet — add one below.</span>}
          </div>
          <div className="flex" style={{ gap: 8, flexWrap: "wrap" }}>
            <input
              className="input"
              style={{ height: 38, width: 260 }}
              placeholder="Add a topic, e.g. legal tech India"
              value={input}
              maxLength={100}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") add(); }}
            />
            <button className="btn btn-ghost btn-sm" onClick={add} disabled={!input.trim() || draft.length >= 10}><Icon name="plus" size={14} />Add</button>
            <button className="btn btn-primary btn-sm" onClick={save} disabled={saving || !draft.length}><Icon name="check" size={14} />{saving ? "Saving…" : "Save topics"}</button>
            {draft.length >= 10 && <span className="faint" style={{ fontSize: 12, alignSelf: "center" }}>Max 10 topics.</span>}
          </div>
        </>
      )}
    </div>
  );
}

function PageNews({ toast }) {
  const [news, setNews] = useState(null);      // null=loading, false=unavailable, object=loaded
  const [topicIdx, setTopicIdx] = useState(0);
  const [nonce, setNonce] = useState(0);       // bump to refetch
  const [showTopics, setShowTopics] = useState(false);

  useEffect(() => {
    let alive = true;
    fetch("/api/news", { headers: { "content-type": "application/json" } })
      .then((res) => (res.ok ? res.json() : Promise.reject(new Error(String(res.status)))))
      .then((body) => { if (alive) setNews(body?.news || false); })
      .catch(() => { if (alive) setNews(false); });
    return () => { alive = false; };
  }, [nonce]);

  const topics = Array.isArray(news?.topics) ? news.topics : [];
  const current = topics[Math.min(topicIdx, Math.max(topics.length - 1, 0))] || null;
  const items = current?.items || [];

  return (
    <div className="page">
      <PageHead
        eyebrow="Sylar's WorkSpace"
        title="News"
        sub="Engagement-ranked stories from the last 30 days across Reddit, Hacker News, GitHub, and more."
      >
        <button className="btn btn-ghost" onClick={() => setShowTopics((s) => !s)}>
          <Icon name="edit" size={16} />{showTopics ? "Hide topics" : "Manage topics"}
        </button>
        <button className="btn btn-ghost" onClick={() => { setNews(null); setNonce((n) => n + 1); }}>
          <Icon name="refresh" size={16} />Refresh
        </button>
      </PageHead>

      {showTopics && <TopicsEditor toast={toast || (() => {})} />}

      <div className="card">
        <div className="card-head">
          <div>
            <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>
              {current?.topic || "News Feed"}
            </h2>
            {news && news.generatedAt && (
              <p className="mono" style={{ fontSize: 12 }}>
                updated {newsRelTime(news.generatedAt)} · refreshed every 8h by the news-digest job
              </p>
            )}
          </div>
          {topics.length > 1 && (
            <div className="seg">
              {topics.map((t, i) => (
                <button key={t.topic || i} className={cls(i === topicIdx && "on")} onClick={() => setTopicIdx(i)}>{t.topic}</button>
              ))}
            </div>
          )}
        </div>

        {news === null && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>Loading news…</div>}
        {news !== null && !items.length && (
          <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>
            {current?.error
              ? `Fetch failed: ${current.error}`
              : "No news yet — the news-digest automation job populates this feed on its next run."}
          </div>
        )}
        {current?.staleError && (
          <div className="faint" style={{ fontSize: 12, padding: "0 2px 8px" }}>
            Showing older results from {newsRelTime(current.generatedAt) || "a previous run"} — latest fetch failed.
          </div>
        )}
        {items.length > 0 && (
          <div className="rows">
            {items.map((item, i) => <NewsRow key={item.url || i} item={item} />)}
          </div>
        )}
      </div>
    </div>
  );
}
window.PageNews = PageNews;
