import rss from '@astrojs/rss';
import type { APIRoute } from 'astro';
import ogDefault from '../assets/og-default.jpg';
import {
  absoluteUrl,
  articlePath,
  entrySlug,
  getPublishedPosts,
  optimizeOgImage,
  site,
} from '../lib/site';

// Escape characters that would otherwise break XML parsing inside text nodes
// (the `customData` strings are inserted as-is by @astrojs/rss).
function escapeXml(value: string) {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

// Format a Date as `YYYY-MM-DD` in UTC for use inside tag: URIs.
function isoDate(date: Date) {
  return date.toISOString().slice(0, 10);
}

export const GET: APIRoute = async (context) => {
  const posts = await getPublishedPosts();
  const feedUrl = absoluteUrl('/rss.xml');
  const channelImage = await optimizeOgImage(ogDefault);
  const channelImageUrl = absoluteUrl(channelImage.src);
  const creator = escapeXml(site.name);

  return rss({
    title: site.name,
    description: site.description,
    site: context.site ?? site.url,
    xmlns: {
      atom: 'http://www.w3.org/2005/Atom',
      content: 'http://purl.org/rss/1.0/modules/content/',
      dc: 'http://purl.org/dc/elements/1.1/',
    },
    customData: [
      '<language>en-us</language>',
      `<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>`,
      `<atom:link href="${feedUrl}" rel="self" type="application/rss+xml" />`,
      '<image>',
      `  <url>${channelImageUrl}</url>`,
      `  <title>${escapeXml(site.name)}</title>`,
      `  <link>${site.url}</link>`,
      '</image>',
    ].join('\n'),
    items: posts.map((post) => {
      const url = absoluteUrl(articlePath(post));
      // Stable tag: URI keeps the GUID constant across path renames
      // (e.g. the `/writing/` → `/articles/` migration). The date is the
      // original publish date so re-publishing won't change the GUID.
      const guid = `tag:schmelczer.dev,${isoDate(post.data.date)}:posts/${entrySlug(post)}`;
      const updated = post.data.updated
        ? `<atom:updated>${post.data.updated.toISOString()}</atom:updated>`
        : '';
      return {
        title: post.data.title,
        description: post.data.description,
        pubDate: post.data.date,
        link: url,
        author: `${site.email} (${site.name})`,
        categories: [...post.data.tags],
        customData: [
          `<guid isPermaLink="false">${escapeXml(guid)}</guid>`,
          `<dc:creator>${creator}</dc:creator>`,
          updated,
        ]
          .filter(Boolean)
          .join(''),
      };
    }),
  });
};
