/**
 * Cross-source thread dedup — used by both the /api/now route (display)
 * and the check-scheduler tick (per-item check pool). The same logic in
 * both places means: a Linear AWS-456 mention in MEMORY.md and the
 * AWS-456 row from Linear MCP discovery render once and get checked once.
 *
 * Two passes:
 *   1. Exact itemKey match (file + line + text-prefix) — same row, same source.
 *   2. Cross-source ref match — Linear issue id, Notion page id extracted
 *      from text or location. First-seen wins, so callers should pass
 *      MEMORY items first to keep Pavel's annotation context.
 */
import type { NowItem } from "./now";
import { itemKey } from "./check-cache";

/** Patterns that yield a cross-source canonical reference (uppercased). */
const REF_RES = [
  /\b([A-Z]{2,5}-\d{2,6})\b/g,                                  // Linear-style: AWS-456, FE-12
  /\bnotion\.so\/([a-zA-Z0-9-]+)\b/g,                           // Notion page ids in URLs
  /\blinear\.app\/[^\/\s]+\/issue\/([A-Z]{2,5}-\d{2,6})/gi,     // Linear issue URLs
];

export function extractRefs(text: string, location: string | undefined): string[] {
  const refs = new Set<string>();
  const haystack = `${text} ${location ?? ""}`;
  for (const re of REF_RES) {
    re.lastIndex = 0;
    let m: RegExpExecArray | null;
    while ((m = re.exec(haystack)) !== null) refs.add(m[1].toUpperCase());
  }
  return [...refs];
}

export function mergeThreads(items: NowItem[]): NowItem[] {
  const seenKeys = new Set<string>();
  const seenRefs = new Set<string>();
  const out: NowItem[] = [];
  for (const it of items) {
    const key = itemKey(it.file, it.line, it.text);
    if (seenKeys.has(key)) continue;
    const refs = extractRefs(it.text, (it as { location?: string }).location);
    if (refs.some((r) => seenRefs.has(r))) continue;
    seenKeys.add(key);
    refs.forEach((r) => seenRefs.add(r));
    out.push(it);
  }
  return out;
}
