import type { RSSFeedItem } from "@astrojs/rss";
import { getEntries, type CollectionEntry, type CollectionKey } from "astro:content";
import { experimental_AstroContainer } from "astro/container";
import sanitizeHtml from "sanitize-html";
import { getUsernameForLang } from "./get_username_for_lang";
import { markdownToPlaintext } from "./markdown_to_plaintext";
import { qualifyLocalURLsInMarkdown } from "./qualify_local_urls_in_markdown";
import { markdown } from "@astropub/md";
import { t, type Lang } from "@i18n";
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
import mdxRenderer from "astro/jsx/server.js";
import { htmlToAbsoluteUrls } from "./html_to_absolute_urls";
import { formatCopyrightedCharacters } from "./format_copyrighted_characters";

export type FeedItem = RSSFeedItem &
  Required<Pick<RSSFeedItem, "title" | "pubDate" | "link" | "description" | "categories" | "content">>;

export type EntryWithPubDate<C extends CollectionKey> = CollectionEntry<C> & { data: { pubDate: Date } };

const container = await experimental_AstroContainer.create();
container.addServerRenderer({ renderer: mdxRenderer } as any);

function toNoonUTCDate(date: Date) {
  const adjustedDate = new Date(date);
  adjustedDate.setUTCHours(12);
  return adjustedDate;
}

const getLinkForUser = (user: CollectionEntry<"users">, lang: Lang) => {
  const userName = getUsernameForLang(user, lang);
  if (user.data.preferredLink) {
    return `<a href="${user.data.links[user.data.preferredLink]!.link}">${userName}</a>`;
  }
  return userName;
};

export async function storyFeedItem(
  site: URL,
  data: EntryWithPubDate<"stories">["data"],
  slug: CollectionEntry<"stories">["slug"],
  content: AstroComponentFactory,
): Promise<FeedItem> {
  const copyrightedCharacters = await formatCopyrightedCharacters(data.copyrightedCharacters);
  return {
    title: `New story! "${data.title}"`,
    pubDate: toNoonUTCDate(data.pubDate),
    link: `/stories/${slug}`,
    description:
      `${t(data.lang, "story/warnings", data.wordCount, data.contentWarning)}\n\n${markdownToPlaintext(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))}`.replaceAll(
        /[\n ]+/g,
        " ",
      ),
    categories: ["story"],
    commentsUrl: data.posts.mastodon?.link,
    content: sanitizeHtml(
      htmlToAbsoluteUrls(
        `<h1>${data.title}</h1>` +
          `<p>${t(
            data.lang,
            "story/authors",
            (await getEntries(data.authors)).map((author) => getLinkForUser(author, data.lang)),
          )}</p>` +
          (data.requesters
            ? `<p>${t(
                data.lang,
                "story/requested_by",
                (await getEntries(data.requesters)).map((requester) => getLinkForUser(requester, data.lang)),
              )}</p>`
            : "") +
          (data.commissioners
            ? `<p>${t(
                data.lang,
                "story/commissioned_by",
                (await getEntries(data.commissioners)).map((commissioner) => getLinkForUser(commissioner, data.lang)),
              )}</p>`
            : "") +
          `<hr><p><em>${t(data.lang, "story/warnings", data.wordCount, data.contentWarning)}</em></p>` +
          `<hr>` +
          (await container.renderToString(content)) +
          `<hr><h2>Description</h2>` +
          (await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))) +
          (copyrightedCharacters.length > 0
            ? `<br /><ul>${copyrightedCharacters.map(
                ({ user, characters }) =>
                  "<li>" +
                  t(
                    data.lang,
                    "characters/characters_are_copyrighted_by",
                    getLinkForUser(user, data.lang),
                    characters,
                  ) +
                  "</li>",
              )}</ul>`
            : ""),
        site,
      ),
      {
        allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
      },
    ),
  };
}

export async function gameFeedItem(
  site: URL,
  data: EntryWithPubDate<"games">["data"],
  slug: CollectionEntry<"games">["slug"],
  content: AstroComponentFactory,
): Promise<FeedItem> {
  const copyrightedCharacters = await formatCopyrightedCharacters(data.copyrightedCharacters);
  return {
    title: `New game! "${data.title}"`,
    pubDate: toNoonUTCDate(data.pubDate),
    link: `/games/${slug}`,
    description: `${t(data.lang, "game/warnings", data.platforms, data.contentWarning)}\n\n${markdownToPlaintext(
      await qualifyLocalURLsInMarkdown(data.description, data.lang, site),
    )}`.replaceAll(/[\n ]+/g, " "),
    categories: ["game"],
    commentsUrl: data.posts.mastodon?.link,
    content: sanitizeHtml(
      htmlToAbsoluteUrls(
        `<h1>${data.title}</h1>` +
          `<p>${t(
            data.lang,
            "story/authors",
            (await getEntries(data.authors)).map((author) => getLinkForUser(author, data.lang)),
          )}</p>` +
          `<hr><p>${t(data.lang, "game/platforms", data.platforms)}</p>` +
          `<hr><p><em>${data.contentWarning}</em></p>` +
          `<hr>` +
          (await container.renderToString(content)) +
          `<hr><h2>Description</h2>` +
          (await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))) +
          (copyrightedCharacters.length > 0
            ? `<br /><ul>${copyrightedCharacters.map(
                ({ user, characters }) =>
                  "<li>" +
                  t(
                    data.lang,
                    "characters/characters_are_copyrighted_by",
                    getLinkForUser(user, data.lang),
                    characters,
                  ) +
                  "</li>",
              )}</ul>`
            : ""),
        site,
      ),
      {
        allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
      },
    ),
  };
}

export async function blogFeedItem(
  site: URL,
  data: EntryWithPubDate<"blog">["data"],
  slug: CollectionEntry<"blog">["slug"],
  content: AstroComponentFactory,
): Promise<FeedItem> {
  return {
    title: `New blog post! "${data.title}"`,
    pubDate: toNoonUTCDate(data.pubDate),
    link: `/blog/${slug}`,
    description: markdownToPlaintext(await qualifyLocalURLsInMarkdown(data.description, data.lang, site)).replaceAll(
      /[\n ]+/g,
      " ",
    ),
    categories: ["blog post"],
    commentsUrl: data.posts.mastodon?.link,
    content: sanitizeHtml(
      htmlToAbsoluteUrls(
        `<h1>${data.title}</h1>` +
          `<p>${t(
            data.lang,
            "story/authors",
            (await getEntries(data.authors)).map((author) => getLinkForUser(author, data.lang)),
          )}</p>` +
          `<hr>` +
          (await markdown(await qualifyLocalURLsInMarkdown(data.description, data.lang, site))) +
          `<hr>` +
          (await container.renderToString(content)),
        site!,
      ),
      {
        allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
      },
    ),
  };
}
