#!/usr/bin/env node
/**
 * @agent-manifest
 * name:        news-radar
 * title:       News Radar
 * class:       content
 * trigger:     manual / cron-capable (maintains news-queue.json for the writer)
 * description: Discover timely niche news and maintain news-queue.json; the daily writer drains it first.
 * model:       research=claude-opus-4-8
 * prompt:      prompts/news-radar.system.md
 * skills:      -
 * tools:       lib/claude.mjs
 * reads:       -
 * writes:      -
 * created:     2026-07-04
 * @end
 */
// News radar: discover timely Medellín food news/events and maintain news-queue.json.
// Usage: node agents/scripts/news-radar.mjs
import { existsSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
import { dirname, resolve, join } from "node:path";
import { fileURLToPath } from "node:url";
import { runClaude } from "./lib/claude.mjs";
import { loadJson, todayInTz, addDays } from "./lib/util.mjs";

const HERE = dirname(fileURLToPath(import.meta.url));
const PIPELINE = resolve(HERE, "..");
const cfg = loadJson(join(PIPELINE, "config.json"));
const root = cfg.paths.projectRoot;

const queuePath = join(root, cfg.paths.newsDir, "news-queue.json");
const scanPath = join(root, cfg.paths.newsDir, "news-scan.json");

const loadArr = (p) => (existsSync(p) ? loadJson(p) : []);
const publishedSlugs = () => {
  const d = join(root, cfg.paths.blogContentDir);
  return existsSync(d)
    ? readdirSync(d, { withFileTypes: true }).filter((x) => x.isDirectory()).map((x) => x.name)
    : [];
};
const calendarSlugs = () => {
  const p = join(root, cfg.paths.calendar);
  return existsSync(p) ? loadJson(p).map((e) => e.slug) : [];
};

/** Scan for fresh news and merge into news-queue.json. Returns the merged queue. */
export async function runNewsRadar() {
  if (!cfg.news?.enabled) {
    console.log("[news] news.enabled is false — skipping.");
    return loadArr(queuePath);
  }
  const today = todayInTz(cfg.editorial.timezone);
  const recencyDays = cfg.news.recencyDays ?? 5;
  const maxPerScan = cfg.news.maxPerScan ?? 8;

  const existing = loadArr(queuePath);
  const known = new Set([...existing.map((i) => i.slug), ...publishedSlugs(), ...calendarSlugs()]);

  const prompt = `Find timely Medellín food/restaurant news to publish about now.

- Today: ${today}
- Prefer items from the last ~2-3 weeks. Return at most ${maxPerScan} items (fewer is fine; [] if nothing fresh).
- Write the JSON array to: ${scanPath}
- Do NOT reuse any of these existing slugs: ${[...known].join(", ") || "(none)"}
- Existing published posts: ${join(root, cfg.paths.blogContentDir)}

Follow the output contract in your system prompt exactly.`;

  await runClaude({
    prompt,
    systemPromptFile: join(PIPELINE, "prompts/news-radar.system.md"),
    model: cfg.models.research,
    allowedTools: "Read Glob Grep WebSearch Write",
    addDirs: [root],
    cwd: root,
    logFile: join(root, cfg.paths.logsDir, `news-${today}.log`),
  });

  const scanned = loadArr(scanPath);

  // Prune: drop stale FRESH items (older than recency window); keep used items for dedup history.
  const cutoff = addDays(today, -recencyDays);
  const kept = existing.filter((i) => i.status === "used" || (i.discovered || today) >= cutoff);
  const keptSlugs = new Set(kept.map((i) => i.slug));
  const dedupe = new Set([...keptSlugs, ...publishedSlugs(), ...calendarSlugs()]);

  let added = 0;
  for (const item of scanned) {
    if (!item?.slug || dedupe.has(item.slug)) continue;
    kept.push({ ...item, discovered: item.discovered || today, status: "fresh" });
    dedupe.add(item.slug);
    added++;
  }
  // Newest + hottest first.
  kept.sort((a, b) => (b.freshnessScore ?? 0) - (a.freshnessScore ?? 0) || (b.discovered || "").localeCompare(a.discovered || ""));
  writeFileSync(queuePath, JSON.stringify(kept, null, 2));

  const fresh = kept.filter((i) => i.status === "fresh").length;
  console.log(`[news] scanned ${scanned.length}, added ${added}, queue now ${kept.length} (${fresh} fresh).`);
  return kept;
}

/** The freshest unused news item within the recency window, or null. */
export function pickFreshNews(cfg2 = cfg) {
  if (!cfg2.news?.enabled || !existsSync(queuePath)) return null;
  const today = todayInTz(cfg2.editorial.timezone);
  const cutoff = addDays(today, -(cfg2.news.recencyDays ?? 5));
  const fresh = loadJson(queuePath)
    .filter((i) => i.status === "fresh" && (i.discovered || today) >= cutoff)
    .sort((a, b) => (b.freshnessScore ?? 0) - (a.freshnessScore ?? 0) || (b.discovered || "").localeCompare(a.discovered || ""));
  return fresh[0] || null;
}

/** Mark a news item used (after it's been drafted). */
export function markNewsUsed(slug) {
  if (!existsSync(queuePath)) return;
  const q = loadJson(queuePath);
  const item = q.find((i) => i.slug === slug);
  if (item) {
    item.status = "used";
    writeFileSync(queuePath, JSON.stringify(q, null, 2));
  }
}

// CLI
if (import.meta.url === `file://${process.argv[1]}`) {
  await runNewsRadar();
}
