---
import type { ImageMetadata } from "astro";
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
import { Image } from "astro:assets";
import GalleryLayout from "@layouts/GalleryLayout.astro";
import { t, type Lang } from "@i18n";
import UserComponent from "@components/UserComponent.astro";
import { markdownToPlaintext } from "@utils/markdown_to_plaintext";
import { IconNoOneUnder18, IconSparkles, IconSquareRSS } from "@components/icons";

const MAX_FEATURED_ITEMS = 4;
const MAX_LATEST_ITEMS = 10;

interface LatestItemsEntry {
  type: string;
  isAgeRestricted: boolean;
  thumbnail?: ImageMetadata;
  href: string;
  title: string;
  lang: Lang;
  authors: CollectionEntry<"users">[];
  altText: string;
  pubDate: Date;
}

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

const latestStories = (
  (await getCollection(
    "stories",
    (story) => !story.data.isDraft && story.data.pubDate,
  )) as EntryWithPubDate<"stories">[]
)
  .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
  .slice(0, MAX_LATEST_ITEMS);
const latestGames = (
  (await getCollection("games", (game) => !game.data.isDraft && game.data.pubDate)) as EntryWithPubDate<"games">[]
)
  .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
  .slice(0, MAX_LATEST_ITEMS);
const latestPosts = (
  (await getCollection("blog", (post) => !post.data.isDraft && post.data.pubDate)) as EntryWithPubDate<"blog">[]
)
  .sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
  .slice(0, MAX_LATEST_ITEMS);

const featuredStories = (
  (await getCollection(
    "stories",
    (story) => !story.data.isDraft && story.data.pubDate && story.data.isFeatured,
  )) as EntryWithPubDate<"stories">[]
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
const featuredGames = (
  (await getCollection(
    "games",
    (game) => !game.data.isDraft && game.data.pubDate && game.data.isFeatured,
  )) as EntryWithPubDate<"games">[]
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
const featuredPosts = (
  (await getCollection(
    "blog",
    (post) => !post.data.isDraft && post.data.pubDate && post.data.isFeatured,
  )) as EntryWithPubDate<"blog">[]
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());

async function storyEntry(story: EntryWithPubDate<"stories">): Promise<LatestItemsEntry> {
  return {
    type: "Story",
    isAgeRestricted: story.data.isAgeRestricted,
    thumbnail: story.data.thumbnail,
    href: `/stories/${story.slug}`,
    title: story.data.title,
    authors: await getEntries(story.data.authors),
    lang: story.data.lang,
    altText: t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning),
    pubDate: story.data.pubDate,
  } satisfies LatestItemsEntry;
}

async function gameEntry(game: EntryWithPubDate<"games">): Promise<LatestItemsEntry> {
  return {
    type: "Game",
    isAgeRestricted: game.data.isAgeRestricted,
    thumbnail: game.data.thumbnail,
    href: `/games/${game.slug}`,
    title: game.data.title,
    authors: await getEntries(game.data.authors),
    lang: game.data.lang,
    altText: t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning),
    pubDate: game.data.pubDate,
  } satisfies LatestItemsEntry;
}

async function blogEntry(post: EntryWithPubDate<"blog">): Promise<LatestItemsEntry> {
  return {
    type: "Blog post",
    isAgeRestricted: post.data.isAgeRestricted,
    thumbnail: post.data.thumbnail,
    href: `/blog/${post.slug}`,
    title: post.data.title,
    authors: await getEntries(post.data.authors),
    lang: post.data.lang,
    altText: markdownToPlaintext(post.data.description),
    pubDate: post.data.pubDate,
  } satisfies LatestItemsEntry;
}

const latestItems: LatestItemsEntry[] = await Promise.all(
  [
    latestStories.map((story) => ({
      date: story.data.pubDate,
      fn: async () => await storyEntry(story),
    })),
    latestGames.map((game) => ({
      date: game.data.pubDate,
      fn: async () => await gameEntry(game),
    })),
    latestPosts.map((post) => ({
      date: post.data.pubDate,
      fn: async () => await blogEntry(post),
    })),
  ]
    .flat()
    .sort((a, b) => b.date.getTime() - a.date.getTime())
    .slice(0, MAX_LATEST_ITEMS)
    .map(async (entry) => await entry.fn()),
);

const featuredItems: LatestItemsEntry[] = await Promise.all(
  [
    featuredStories.map((story) => ({
      date: story.data.pubDate,
      fn: async () => await storyEntry(story),
    })),
    featuredGames.map((game) => ({
      date: game.data.pubDate,
      fn: async () => await gameEntry(game),
    })),
    featuredPosts.map((post) => ({
      date: post.data.pubDate,
      fn: async () => await blogEntry(post),
    })),
  ]
    .flat()
    .sort((a, b) => b.date.getTime() - a.date.getTime())
    .map(async (entry) => await entry.fn()),
);

if (featuredItems.length > MAX_FEATURED_ITEMS) {
  throw new Error(`Cannot have more than ${MAX_FEATURED_ITEMS} featured items (found ${featuredItems.length} items)`);
}
---

<GalleryLayout pageTitle="Gallery" class="h-feed">
  <Fragment slot="head">
    <meta property="og:description" content="Bad Manners || Welcome to my gallery!" />
    <meta name="description" content="Find stories, games, and blog posts made by Bad Manners." />
  </Fragment>
  <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Gallery</h1>
  <hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" aria-hidden />
  <div class="p-summary">
    <div class="my-4 flex">
      <p class="grow">
        Welcome to my corner of the Internet! <a
          class="u-url text-link underline"
          href="https://badmanners.xyz"
          x-data={JSON.stringify({ href: "https://badmanners.xyz", suffix: "?ageVerified=true" })}
          x-bind:href="ageVerified ? href + suffix : href"
          rel="me">Check out my main page</a
        > for more info.
      </p>
      <a class="u-url text-link ml-2 p-1 md:mr-5" href="/feed.xml" rel="alternate" title="RSS feed" data-tooltip>
        <IconSquareRSS width="2rem" height="2rem" />
        <span class="sr-only">RSS feed</span>
      </a>
    </div>
  </div>
  <section
    class="my-2 max-w-max rounded-lg bg-stone-50 px-4 pb-8 pt-4 md:-ml-2 md:pb-10 dark:bg-stone-700"
    aria-labelledby="featured-uploads"
  >
    <h2 id="featured-uploads" class="mb-4 p-2 text-xl font-semibold text-stone-900 dark:text-stone-50">
      <span class="inline-block align-middle">
        <IconSparkles width="1.25rem" height="1.25rem" />
      </span>
      <span>Featured</span>
    </h2>
    <ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
      {
        featuredItems.map((entry) => (
          <li class="h-entry break-inside-avoid" aria-label={entry.title}>
            <a
              class="u-url text-link hover:underline focus:underline"
              href={entry.href}
              title={entry.altText}
              data-tooltip
            >
              {entry.thumbnail ? (
                <div class="flex aspect-square max-w-[192px] justify-center">
                  <Image
                    loading="eager"
                    class="u-photo m-auto"
                    src={entry.thumbnail}
                    alt={`Thumbnail for ${entry.title}`}
                    width={192}
                  />
                </div>
              ) : null}
              <div class="max-w-[192px] text-sm">
                <span class="p-name" aria-label="Title">
                  {entry.title}
                </span>
                <br />
                <span class="italic">
                  {entry.isAgeRestricted ? (
                    <span class="inline-block align-middle" aria-label="Age restricted">
                      <IconNoOneUnder18 width="1.25rem" height="1.25rem" />
                    </span>
                  ) : null}
                  <span class="p-category" aria-label="Category">
                    {entry.type}
                  </span>{" "}
                  &ndash;{" "}
                  <time
                    class="dt-published"
                    datetime={entry.pubDate.toISOString().slice(0, 10)}
                    aria-label="Publish date"
                  >
                    {entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
                  </time>
                </span>
              </div>
            </a>
            <div class="sr-only select-none">
              <p class="p-summary" aria-label="Summary">
                {entry.altText}
              </p>
              <p class="p-language" aria-label="Language">
                {t(entry.lang, "language/language")}
              </p>
              <div aria-label="Authors">
                <span>{entry.authors.length == 1 ? "Author:" : "Authors:"}</span>
                {entry.authors.map((author) => (
                  <UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
                ))}
              </div>
            </div>
          </li>
        ))
      }
    </ul>
  </section>
  <section class="mb-2 mt-12" aria-labelledby="latest-uploads">
    <h2 id="latest-uploads" class="mb-4 p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">
      Latest uploads
    </h2>
    <ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
      {
        latestItems.map((entry) => (
          <li class="h-entry break-inside-avoid" aria-label={entry.title}>
            <a
              class="u-url text-link hover:underline focus:underline"
              href={entry.href}
              title={entry.altText}
              data-tooltip
            >
              {entry.thumbnail ? (
                <div class="flex aspect-square max-w-[192px] justify-center">
                  <Image
                    loading="eager"
                    class="u-photo m-auto"
                    src={entry.thumbnail}
                    alt={`Thumbnail for ${entry.title}`}
                    width={192}
                  />
                </div>
              ) : null}
              <div class="max-w-[192px] text-sm">
                <span class="p-name" aria-label="Title">
                  {entry.title}
                </span>
                <br />
                <span class="italic">
                  {entry.isAgeRestricted ? (
                    <span class="inline-block align-middle" aria-label="Age restricted">
                      <IconNoOneUnder18 width="1.25rem" height="1.25rem" />
                    </span>
                  ) : null}
                  <span class="p-category" aria-label="Category">
                    {entry.type}
                  </span>{" "}
                  &ndash;{" "}
                  <time
                    class="dt-published"
                    datetime={entry.pubDate.toISOString().slice(0, 10)}
                    aria-label="Publish date"
                  >
                    {entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
                  </time>
                </span>
              </div>
            </a>
            <div class="sr-only select-none">
              <p class="p-summary" aria-label="Summary">
                {entry.altText}
              </p>
              <p class="p-language" aria-label="Language">
                {t(entry.lang, "language/language")}
              </p>
              <div aria-label="Authors">
                <span>{entry.authors.length == 1 ? "Author:" : "Authors:"}</span>
                {entry.authors.map((author) => (
                  <UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
                ))}
              </div>
            </div>
          </li>
        ))
      }
    </ul>
  </section>
</GalleryLayout>
