import {
  docsSource,
  guidesSource,
  tutorialsSource,
  integrationsSource,
  changelogSource,
  blogSource,
  getPageMarkdownUrl,
  getPageImage,
  getBlogPageImage,
} from '@/lib/source';
import { createSection } from '@/lib/section';
import BlogPostMeta from '@/src/components/BlogPostMeta';
import ClaudeCodeTerminal from '@/src/sections/home/ClaudeCodeTerminal';
import SchemaInjector from '@/src/components/SchemaInjector/SchemaInjector';
import {
  buildArticleSchema,
  buildBlogHomeSchema,
} from '@/src/utils/schema-helpers';
import { getAuthor, type AuthorId } from '@/lib/authors';
import type { BlogCategoryId } from '@/lib/blog-categories';

type BlogFrontmatter = {
  title: string;
  description?: string;
  authors?: AuthorId[];
  date?: Date | string;
  category?: BlogCategoryId;
  lastModified?: number | string | Date | null;
  // Optional per-post cover image (absolute URL). When present it
  // overrides the site-wide `og:image` fallback set in `app/layout.tsx`
  // so social previews show the post's hero art instead of the generic
  // social card. Validated in `blogPageSchema` (source.config.ts).
  image?: string;
};

/**
 * Pull the publish / modified dates off a blog page as ISO strings.
 * `date` is author-supplied frontmatter; `lastModified` is injected by
 * the `fumadocs-mdx/plugins/last-modified` plugin (git-derived).
 */
function toIso(value: unknown): string | undefined {
  if (!value) return undefined;
  if (value instanceof Date) return value.toISOString();
  const parsed = new Date(value as string);
  return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();
}

export const docsSection = createSection({
  source: docsSource,
  contentDir: 'content/docs',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, docsSource).url,
  getImageUrl: (page) => getPageImage(page).url,
  showContributors: true,
});

export const guidesSection = createSection({
  source: guidesSource,
  contentDir: 'content/guides',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, guidesSource).url,
  showContributors: true,
  // Guides-only global MDX components — authors can use these in any
  // guide without an explicit `import`. Scoped to guides so they don't
  // leak into docs/tutorials/etc.
  mdxComponents: { ClaudeCodeTerminal },
});

export const tutorialsSection = createSection({
  source: tutorialsSource,
  contentDir: 'content/tutorials',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, tutorialsSource).url,
  showContributors: true,
});

export const integrationsSection = createSection({
  source: integrationsSource,
  contentDir: 'content/integrations',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, integrationsSource).url,
  showContributors: true,
});

export const changelogSection = createSection({
  source: changelogSource,
  contentDir: 'content/changelog',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, changelogSource).url,
});

export const blogSection = createSection({
  source: blogSource,
  contentDir: 'content/blog',
  getMarkdownUrl: (page) => getPageMarkdownUrl(page, blogSource).url,
  // Per-post `og:image` fallback: a generated title/description card
  // served from `/og/blog/<slug>/image.png`. A post's `image:`
  // frontmatter still wins — `extendMetadata` sets `openGraph.images`
  // to it, and `generateMetadata` merges that over this base. Without
  // this fallback, posts with no `image:` emit no `og:image` (the blog
  // `openGraph` override clobbers the root layout's default), so social
  // scrapers like LinkedIn grab the author avatar off the page instead.
  getImageUrl: (page) => getBlogPageImage(page).url,
  // Blog-only global MDX components — authors can use these in any post
  // without an explicit `import`. Scoped to the blog so they don't leak
  // into docs/guides/etc.
  mdxComponents: { ClaudeCodeTerminal },
  renderBeforeBody: (page) => {
    const data = page.data as BlogFrontmatter;
    const { authors, category, title, description, date } = data;

    // Blog index (`/blog`) — no authors/date; emit a `Blog` JSON-LD
    // listing all posts instead so Google can surface the post set
    // directly. Matches what the old Docusaurus blog plugin emitted.
    if (!authors) {
      const posts = blogSource
        .getPages()
        .filter((p) => {
          const d = p.data as BlogFrontmatter;
          return Array.isArray(d.authors) && d.authors.length > 0;
        })
        .map((p) => {
          const d = p.data as BlogFrontmatter;
          return {
            title: d.title,
            description: d.description ?? '',
            slug: p.slugs[p.slugs.length - 1] ?? '',
            authors: (d.authors ?? []).map((id) => getAuthor(id).name),
            date: toIso(d.date) ?? '',
          };
        });
      return <SchemaInjector schema={buildBlogHomeSchema(posts)} />;
    }

    // Per-post byline (unchanged) + Article / TechArticle JSON-LD.
    // `date` is still required in frontmatter for the git-less publish
    // sort / OG metadata, but we don't display it in the byline row.
    const authorNames = authors.map((id) => getAuthor(id).name);
    const articleSchema = buildArticleSchema({
      title,
      description,
      url: page.url,
      datePublished: toIso(date),
      dateModified: toIso(data.lastModified ?? undefined),
      authors: authorNames,
    });

    return (
      <>
        <SchemaInjector schema={articleSchema} />
        <BlogPostMeta authors={authors} category={category} />
      </>
    );
  },
  // Individual posts get `openGraph.type = 'article'` + publish /
  // modified timestamps + author list, so social previews render as
  // proper article cards instead of a generic website card. If the
  // post sets `image:` in frontmatter we also promote it to
  // `openGraph.images` / `twitter.images` so the share card shows the
  // post's hero art instead of the generic site-wide social_card.png.
  extendMetadata: (page) => {
    const data = page.data as BlogFrontmatter;
    if (!data.authors) return {};

    const publishedTime = toIso(data.date);
    const modifiedTime = toIso(data.lastModified ?? undefined);
    const authorNames = data.authors.map((id) => getAuthor(id).name);
    const image = data.image;

    return {
      openGraph: {
        type: 'article',
        ...(publishedTime ? { publishedTime } : {}),
        ...(modifiedTime ? { modifiedTime } : {}),
        authors: authorNames,
        // Per-post hero art overrides the site-wide `/img/social_card.png`
        // default set in `app/layout.tsx`. We intentionally DO NOT also
        // override `twitter.images` here: Next.js replaces (doesn't
        // deep-merge) the `twitter` object across nested `generateMetadata`
        // calls, so setting it would also wipe the layout's `card`,
        // `site`, and `creator`. X/Twitter's card renderer falls back
        // to `og:image` when `twitter:image` is absent, and other
        // `summary_large_image` consumers (LinkedIn, Slack, Discord)
        // read `og:image` directly — so the single override covers
        // every surface.
        ...(image ? { images: image } : {}),
      },
    };
  },
});
