import type { APIRoute, GetStaticPaths } from "astro";
import { getCollection, type CollectionEntry, getEntries } from "astro:content";
import type { PostWebsite } from "@content/config";
import { t } from "@i18n";
import { formatCopyrightedCharacters } from "@utils/format_copyrighted_characters";
import { markdownToBbcode } from "@utils/markdown_to_bbcode";
import { getUsernameForLang } from "@utils/get_username_for_lang";
import { isAnonymousUser } from "@utils/is_anonymous_user";
import { qualifyLocalURLsInMarkdown } from "@utils/qualify_local_urls_in_markdown";
import { getWebsiteLinkForUser } from "@utils/get_website_link_for_user";
import { toPlainMarkdown } from "@utils/to_plain_markdown";

interface ExportWebsiteInfo {
  website: PostWebsite;
  exportFormat: "bbcode" | "markdown";
}

const WEBSITE_LIST = [
  { website: "eka", exportFormat: "bbcode" },
  { website: "furaffinity", exportFormat: "bbcode" },
  { website: "inkbunny", exportFormat: "bbcode" },
  { website: "sofurry", exportFormat: "bbcode" },
  { website: "weasyl", exportFormat: "markdown" },
] as const satisfies ExportWebsiteInfo[];

type ExportWebsiteName = typeof WEBSITE_LIST extends ReadonlyArray<{ website: infer K }> ? K : never;

export type ExportStoryResponse = {
  story: string;
  description: Record<string, string>;
  thumbnail: string | null;
};

type Props = {
  story: CollectionEntry<"stories">;
};

type Params = {
  slug: CollectionEntry<"stories">["slug"];
};

export const getStaticPaths: GetStaticPaths = async () => {
  if (import.meta.env.PROD) {
    return [];
  }
  return (await getCollection("stories")).map((story) => ({
    params: { slug: story.slug } satisfies Params,
    props: { story } satisfies Props,
  }));
};

export const GET: APIRoute<Props, Params> = async ({ props: { story }, site }) => {
  try {
    const { lang } = story.data;
    const copyrightedCharacters = await formatCopyrightedCharacters(story.data.copyrightedCharacters);
    const authorsList = await getEntries(story.data.authors);
    const commissionersList = story.data.commissioners && (await getEntries(story.data.commissioners));
    const requestersList = story.data.requesters && (await getEntries(story.data.requesters));

    const description = await Promise.all(
      WEBSITE_LIST.map(async ({ website, exportFormat }) => {
        const exportWebsite: ExportWebsiteName = website;
        const u = (user: CollectionEntry<"users">) =>
          isAnonymousUser(user)
            ? getUsernameForLang(user, lang)
            : getWebsiteLinkForUser(user, exportWebsite, (user) => getUsernameForLang(user, lang));
        const storyDescription = await [
          story.data.description,
          `*${t(lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}*`,
          t(
            lang,
            "export_story/authors",
            authorsList.map((author) => u(author)),
          ),
          requestersList &&
            t(
              lang,
              "export_story/request_for",
              requestersList.map((requester) => u(requester)),
            ),
          commissionersList &&
            t(
              lang,
              "export_story/commissioned_by",
              commissionersList.map((commissioner) => u(commissioner)),
            ),
          ...copyrightedCharacters.map(({ user, characters }) =>
            t(lang, "characters/characters_are_copyrighted_by", u(user), characters[0] === "" ? [] : characters),
          ),
        ].reduce(async (promise, data) => {
          if (!data) {
            return promise;
          }
          const acc = await promise;
          const newData = await qualifyLocalURLsInMarkdown(data, lang, site, exportWebsite);
          return `${acc}\n\n${newData}`;
        }, Promise.resolve(""));
        switch (exportFormat) {
          case "bbcode":
            return {
              descriptionFilename: `description_${exportWebsite}.txt`,
              descriptionText: markdownToBbcode(storyDescription).replaceAll(/\n\n\n+/g, "\n\n"),
            };
          case "markdown":
            return {
              descriptionFilename: `description_${exportWebsite}.md`,
              descriptionText: toPlainMarkdown(storyDescription)
                .replaceAll(/\n\n\n+/g, "\n\n")
                .trim(),
            };
          default:
            const unknown: never = exportFormat;
            throw new Error(`Unknown export format "${unknown}"`);
        }
      }),
    );

    const storyHeader =
      `${story.data.title}\n` +
      `${t(
        lang,
        "story/authors",
        authorsList.map((author) => getUsernameForLang(author, lang)),
      )}\n` +
      (requestersList
        ? `${t(
            lang,
            "story/requested_by",
            requestersList.map((requester) => getUsernameForLang(requester, lang)),
          )}\n`
        : "") +
      (commissionersList
        ? `${t(
            lang,
            "story/commissioned_by",
            commissionersList.map((commissioner) => getUsernameForLang(commissioner, lang)),
          )}\n`
        : "");

    const storyText = `${storyHeader}\n===\n\n${story.body.replaceAll(/\\([=*])/g, "$1")}`
      .replaceAll(/\n\n\n+/g, "\n\n")
      .trim();

    return new Response(
      JSON.stringify({
        story: storyText,
        description: description.reduce(
          (acc, item) => {
            acc[item.descriptionFilename] = item.descriptionText;
            return acc;
          },
          {} as Record<string, string>,
        ),
        thumbnail: story.data.thumbnail ? story.data.thumbnail.src : null,
      } satisfies ExportStoryResponse),
      { headers: { "Content-Type": "application/json; charset=utf-8" } },
    );
  } catch (e) {
    return new Response(
      JSON.stringify({
        message: (e as Error).message ?? null,
        stack: (e as Error).stack ?? null,
      }),
      { status: 500, headers: { "Content-Type": "application/json; charset=utf-8" } },
    );
  }
};
