import { readdirSync, readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import sitemap from '@astrojs/sitemap';
import { defineConfig } from 'astro/config';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeSlug from 'rehype-slug';

// Build a lookup of post slugs to their last modification dates so the sitemap
// can advertise accurate <lastmod> values to crawlers. We parse the markdown
// frontmatter ourselves rather than importing `astro:content` (a virtual module
// that may not be available inside the config). Failures are non-fatal —
// sitemap entries simply fall back to no lastmod.
const postLastmodLookup = new Map();
try {
  const postsDir = path.resolve(
    path.dirname(fileURLToPath(import.meta.url)),
    'src/content/posts'
  );
  for (const entry of readdirSync(postsDir, { withFileTypes: true })) {
    if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
    const slug = entry.name.replace(/\.md$/, '');
    const raw = readFileSync(path.join(postsDir, entry.name), 'utf8');
    const frontmatterMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
    if (!frontmatterMatch) continue;
    const frontmatter = frontmatterMatch[1];
    const updatedMatch = frontmatter.match(/^updated:\s*(.+?)\s*$/m);
    const dateMatch = frontmatter.match(/^date:\s*(.+?)\s*$/m);
    const rawDate = (updatedMatch ?? dateMatch)?.[1]?.replace(/^['"]|['"]$/g, '');
    if (!rawDate) continue;
    const parsed = new Date(rawDate);
    if (!Number.isNaN(parsed.valueOf())) postLastmodLookup.set(slug, parsed);
  }
} catch {
  // Directory missing or unreadable; sitemap will fall back to no lastmod.
}

export default defineConfig({
  site: 'https://schmelczer.dev',
  trailingSlash: 'always',
  build: { inlineStylesheets: 'always' },
  redirects: {
    '/writing/': '/articles/',
    '/writing/[slug]': '/articles/[slug]',
  },
  integrations: [
    sitemap({
      filter: (page) => {
        const path = new URL(page).pathname;
        return !path.startsWith('/writing/') && path !== '/404/';
      },
      serialize(item) {
        const url = new URL(item.url);
        const match = url.pathname.match(/^\/articles\/([^/]+)\/?$/);
        let lastmod = item.lastmod;
        if (match) {
          const date = postLastmodLookup.get(match[1]);
          if (date instanceof Date && !Number.isNaN(date.valueOf())) {
            lastmod = date.toISOString();
          }
        }
        return { ...item, changefreq: 'monthly', ...(lastmod ? { lastmod } : {}) };
      },
    }),
  ],
  image: {
    service: { entrypoint: 'astro/assets/services/sharp' },
  },
  vite: {
    server: {
      watch: {
        // Avoid inotify instance limits in dev containers and mounted volumes.
        usePolling: true,
      },
    },
  },
  markdown: {
    shikiConfig: {
      themes: {
        light: 'github-light',
        dark: 'github-dark',
      },
      defaultColor: false,
      wrap: false,
    },
    rehypePlugins: [
      rehypeSlug,
      [
        rehypeAutolinkHeadings,
        {
          behavior: 'append',
          properties: {
            className: ['heading-anchor'],
            ariaLabel: 'Permalink',
          },
          // Glyph rendered via CSS ::before so it doesn't leak into the TOC
          // when astro:content extracts heading.text from the rendered HTML.
          content: [],
        },
      ],
    ],
  },
});
