/**
 * Telling a link-preview crawler apart from a guest (design D9).
 *
 * Loading `/i/{token}` is what records that a guest opened their invitation. A crawler
 * fetches the same page for an entirely different reason: to build the little preview card
 * a chat application shows. Without this, an admin pasting an invitation link into WhatsApp
 * would have Meta's crawler mark that guest's invitation opened before the guest had seen
 * anything — and the copy-link control exists precisely so that admins do paste links.
 *
 * This is user-agent matching, so it is approximate by nature: the string is self-declared
 * and an unlisted crawler is treated as a guest. That is deliberately the *old* behaviour,
 * so the failure mode is the status quo rather than a regression. Extending the list is one
 * line.
 *
 * The more robust answer is to record the open from the browser after hydration, since no
 * crawler runs JavaScript. That was left out of this change because it rewrites existing
 * lifecycle behaviour and silently drops guests who block scripts — see design D9.
 */

/** Lowercase fragments matched against the user agent. */
export const PREVIEW_CRAWLERS = [
  "facebookexternalhit", // Facebook and Messenger
  "facebookcatalog",
  "whatsapp",
  "twitterbot",
  "slackbot",
  "slack-imgproxy",
  "discordbot",
  "telegrambot",
  "linkedinbot",
  "pinterest",
  "redditbot",
  "embedly",
  "quora link preview",
  "applebot", // iMessage previews and Spotlight
  "skypeuripreview",
  "vkshare",
  "w3c_validator",
  "bingbot",
  "googlebot",
  "developers.google.com/+/web/snippet", // Google's own snippet fetcher
] as const;

/**
 * Whether this request is a link-preview crawler rather than a person.
 *
 * A missing user agent counts as a guest. Crawlers all announce themselves; it is ordinary
 * browsers behind privacy tooling that arrive blank, and treating those as crawlers would
 * silently stop counting real opens.
 */
export function isPreviewCrawler(userAgent: string | null | undefined): boolean {
  if (!userAgent) return false;
  const agent = userAgent.toLowerCase();
  return PREVIEW_CRAWLERS.some((fragment) => agent.includes(fragment));
}
