---
import type { GetStaticPaths } from "astro";
import { Image } from "astro:assets";
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
import { Markdown } from "@astropub/md";
import { slug } from "github-slugger";
import GalleryLayout from "../../layouts/GalleryLayout.astro";
import Prose from "../../components/Prose.astro";
import { t, DEFAULT_LANG } from "../../i18n";
import { qualifyLocalURLsInMarkdown } from "../../utils/qualify_local_urls_in_markdown";
import UserComponent from "../../components/UserComponent.astro";

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

type Props = {
  tag: string;
  description?: string;
  related?: string[];
  stories: (EntryWithPubDate<"stories"> & { authors: CollectionEntry<"users">[] })[];
  games: (EntryWithPubDate<"games"> & { authors: CollectionEntry<"users">[] })[];
};

type Params = {
  slug: string;
};

export const getStaticPaths: GetStaticPaths = async () => {
  const [stories, games, series, tagCategories] = await Promise.all([
    getCollection("stories"),
    getCollection("games"),
    getCollection("series"),
    getCollection("tag-categories"),
  ]);
  const seriesTags = new Set(series.map((s) => s.data.name));
  const tags = new Set<string>();
  stories.forEach((story) => {
    story.data.tags.forEach((tag) => {
      tags.add(tag);
    });
  });
  games.forEach((game) => {
    game.data.tags.forEach((tag) => {
      tags.add(tag);
    });
  });
  const tagDescriptions = tagCategories.reduce(
    (acc, category) => {
      category.data.tags.forEach(({ name, description, related }) => {
        related = related.filter((relatedTag) => {
          if (relatedTag === name) {
            console.warn(`WARNING: Tag "${name}" should not have itself as a related tag; removing...`);
            return false;
          }
          if (!tags.has(relatedTag)) {
            console.warn(`WARNING: Tag "${name}" has an unknown related tag "${relatedTag}"; removing...`);
            return false;
          }
          return true;
        });
        const key = typeof name === "string" ? name : name.en;
        if (key in acc) {
          throw new Error(`Duplicated tag "${key}" found in multiple tag-categories lists`);
        }
        acc[key] = { description, related: related.length > 0 ? related : undefined };
      });
      return acc;
    },
    {} as Record<string, { description?: string; related?: string[] }>,
  );
  return await Promise.all(
    [...tags]
      .filter((tag) => !seriesTags.has(tag))
      .map(async (tag) => ({
        params: { slug: slug(tag) } satisfies Params,
        props: {
          tag,
          description: tagDescriptions[tag]?.description,
          related: tagDescriptions[tag]?.related,
          stories: await Promise.all(
            (
              stories.filter(
                (story) => !story.data.isDraft && story.data.pubDate && story.data.tags.includes(tag),
              ) as EntryWithPubDate<"stories">[]
            )
              .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
              .map(async (story) => ({
                ...story,
                authors: await getEntries(story.data.authors),
              })),
          ),
          games: await Promise.all(
            (
              games.filter(
                (game) => !game.data.isDraft && game.data.pubDate && game.data.tags.includes(tag),
              ) as EntryWithPubDate<"games">[]
            )
              .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
              .map(async (game) => ({
                ...game,
                authors: await getEntries(game.data.authors),
              })),
          ),
        } satisfies Props,
      })),
  );
};

const { props } = Astro;
const description = props.description && (await qualifyLocalURLsInMarkdown(props.description, DEFAULT_LANG));
if (!description) {
  console.warn(`WARNING: Tag "${props.tag}" has no description`);
}
const totalWorksWithTag = t(
  DEFAULT_LANG,
  "tag/total_works_with_tag",
  props.tag,
  props.stories.length,
  props.games.length,
);
---

<GalleryLayout pageTitle={`Works tagged "${props.tag}"`}>
  <meta slot="head" content={`Bad Manners || ${totalWorksWithTag || props.tag}`} property="og:description" />
  <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Works tagged "{props.tag}"</h1>
  <div class="my-4">
    <Prose>
      {description ? <Markdown of={description} /> : null}
      {
        props.related?.length ? (
          <p
            set:html={`See also: ${props.related.map((relatedTag) => `<a href="/tags/${slug(relatedTag)}">${relatedTag}</a>`).join(", ")}.`}
          />
        ) : null
      }
      <p>{totalWorksWithTag}</p>
    </Prose>
  </div>
  {
    props.stories.length > 0 && (
      <section class="my-2" aria-labelledby="content-stories">
        <h2 id="content-stories" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">
          Stories
        </h2>
        <ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
          {props.stories.map((story) => (
            <li class="h-entry break-inside-avoid" lang={story.data.lang}>
              <a
                class="u-url text-link hover:underline focus:underline"
                href={`/stories/${story.slug}`}
                title={t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
              >
                {story.data.thumbnail ? (
                  <div class="flex aspect-square max-w-[192px] justify-center">
                    <Image
                      class="u-photo m-auto"
                      src={story.data.thumbnail}
                      alt={`Thumbnail for ${story.data.title}`}
                      width={192}
                    />
                  </div>
                ) : null}
                <div class="max-w-[192px] text-sm">
                  <span class="p-name" aria-label="Title">
                    {story.data.title}
                  </span>
                  <br />
                  <time
                    class="dt-published italic"
                    datetime={story.data.pubDate.toISOString().slice(0, 10)}
                    aria-label="Publish date"
                  >
                    {story.data.pubDate.toLocaleDateString("en-US", {
                      month: "short",
                      day: "numeric",
                      year: "numeric",
                    })}
                  </time>
                </div>
              </a>
              <div class="sr-only">
                <p class="p-category" aria-label="Category">
                  Story
                </p>
                <p class="p-summary" aria-label="Summary">
                  {t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
                </p>
                <div aria-label="Authors">
                  <span>{story.authors.length == 1 ? "Author:" : "Authors:"}</span>
                  {story.authors.map((author) => (
                    <UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} />
                  ))}
                </div>
              </div>
            </li>
          ))}
        </ul>
      </section>
    )
  }
  {
    props.games.length > 0 && (
      <section class="my-2" aria-labelledby="content-games">
        <h2 id="content-games" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">
          Games
        </h2>
        <ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
          {props.games.map((game) => (
            <li class="h-entry break-inside-avoid" lang={game.data.lang}>
              <a
                class="u-url text-link hover:underline focus:underline"
                href={`/games/${game.slug}`}
                title={t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
              >
                {game.data.thumbnail ? (
                  <div class="flex aspect-[630/500] max-w-[192px] justify-center">
                    <Image
                      class="u-photo m-auto"
                      src={game.data.thumbnail}
                      alt={`Thumbnail for ${game.data.title}`}
                      width={192}
                    />
                  </div>
                ) : null}
                <div class="max-w-[192px] text-sm">
                  <span class="p-name" aria-label="Title">
                    {game.data.title}
                  </span>
                  <br />
                  <time
                    class="dt-published italic"
                    datetime={game.data.pubDate.toISOString().slice(0, 10)}
                    aria-label="Publish date"
                  >
                    {game.data.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
                  </time>
                </div>
              </a>
              <div class="sr-only">
                <p class="p-category" aria-label="Category">
                  Game
                </p>
                <p class="p-summary" aria-label="Summary">
                  {t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
                </p>
                <div aria-label="Authors">
                  <span>{game.authors.length == 1 ? "Author:" : "Authors:"}</span>
                  {game.authors.map((author) => (
                    <UserComponent rel="author" class="p-author" user={author} lang={game.data.lang} />
                  ))}
                </div>
              </div>
            </li>
          ))}
        </ul>
      </section>
    )
  }
</GalleryLayout>
