// Inject Open Graph / Twitter Card tags into published MindSpace HTML at serve time,
// so forwarded links unfurl with a cover image in WeChat / browsers / IM clients.
//
// Source of truth for the cover is the page's own <head> (mindspace-cover JSON /
// <meta name="description"> / hero <img>), reused via extractCoverSignals. Authors who
// already wrote their own og:image are left untouched.
import { extractCoverSignals } from './mindspace-thumbnails.mjs';

function rawTitleFromHtml(html) {
  // Keep the original <title> verbatim (emoji included) for og:title.
  return String(html).match(/<title[^>]*>([^<]*)<\/title>/i)?.[1]?.trim() ?? '';
}

function escapeAttr(value) {
  return String(value)
    .replaceAll('&', '&amp;')
    .replaceAll('"', '&quot;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;');
}

/**
 * Resolve a cover reference (relative path, root-absolute, or full URL) to an
 * absolute https URL the unfurling client can fetch.
 * @returns {string|null} absolute URL, or null when no usable raster cover exists.
 */
function resolveImageUrl(image, { origin, pageDirUrl }) {
  if (!image) return null;
  const trimmed = String(image).trim();
  if (!trimmed || trimmed.startsWith('data:')) return null;
  // og:image must be a raster the client can render; SVG is not honored by WeChat/most unfurlers.
  if (/\.svg(?:[?#]|$)/i.test(trimmed)) return null;
  if (/^https?:\/\//i.test(trimmed)) return trimmed;
  if (trimmed.startsWith('//')) return `https:${trimmed}`;
  if (trimmed.startsWith('/')) return `${origin}${trimmed}`;
  return `${pageDirUrl}${trimmed}`;
}

/**
 * Inject og:/twitter: meta into an HTML document.
 * @param {string} html raw page HTML
 * @param {{ origin: string, pageUrl: string, pageDirUrl: string, fallbackImageUrl?: string }} ctx
 *   origin: https://host  pageUrl: canonical page URL  pageDirUrl: page directory URL (trailing '/')
 *   fallbackImageUrl: absolute raster URL to use when the page has no cover of its own
 * @returns {string} HTML with tags injected (or unchanged when not applicable)
 */
export function injectOgTags(html, { origin, pageUrl, pageDirUrl, fallbackImageUrl = '' }) {
  const source = String(html);
  // Respect a page that already declares its own Open Graph image.
  if (/<meta[^>]+property=["']og:image["']/i.test(source)) return source;

  const signals = extractCoverSignals(source);
  const title = rawTitleFromHtml(source) || signals.title;
  const description = signals.subtitle || '';
  const imageUrl = resolveImageUrl(signals.image, { origin, pageDirUrl }) || fallbackImageUrl || null;

  const tags = [
    '<meta property="og:type" content="article">',
    pageUrl ? `<meta property="og:url" content="${escapeAttr(pageUrl)}">` : '',
    title ? `<meta property="og:title" content="${escapeAttr(title)}">` : '',
    description ? `<meta property="og:description" content="${escapeAttr(description)}">` : '',
    imageUrl ? `<meta property="og:image" content="${escapeAttr(imageUrl)}">` : '',
    `<meta name="twitter:card" content="${imageUrl ? 'summary_large_image' : 'summary'}">`,
    title ? `<meta name="twitter:title" content="${escapeAttr(title)}">` : '',
    description ? `<meta name="twitter:description" content="${escapeAttr(description)}">` : '',
    imageUrl ? `<meta name="twitter:image" content="${escapeAttr(imageUrl)}">` : '',
  ].filter(Boolean);

  const block = `\n${tags.map((t) => `  ${t}`).join('\n')}\n`;
  if (/<\/head>/i.test(source)) {
    return source.replace(/<\/head>/i, `${block}</head>`);
  }
  if (/<head[^>]*>/i.test(source)) {
    return source.replace(/(<head[^>]*>)/i, `$1${block}`);
  }
  return source;
}
