/**
 * Generate one static `learn/<slug>/index.html` shell per lesson, so each lesson is
 * its own indexable URL (`/learn/<slug>/`) carrying lesson-specific `<title>` and
 * `<meta description>`. The shared `src/learn` entry renders the right view from the
 * path. Driven by the lesson `.md` frontmatter — adding a lesson stays "drop a
 * file" — and run from `vite.config.ts` at load (dev and build) so the shells stay
 * in sync; the generated dirs are git-ignored.
 *
 * Plain content-shells only (per-URL metadata, client-rendered body). Prerendering
 * the prose into the HTML would help non-JS crawlers but is deliberately deferred.
 */
import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

const ROOT = new URL('../', import.meta.url);
const LESSONS_DIR = fileURLToPath(new URL('src/learn/lessons/', ROOT));
const LEARN_DIR = fileURLToPath(new URL('learn/', ROOT));

interface LessonMeta {
  slug: string;
  title: string;
  tagline: string;
}

// A minimal frontmatter reader, separate from `core/lesson-parse`'s `parseLesson`:
// this runs at vite-config load time, before the `@core` alias is registered, and
// only needs the three header fields (not the parsed body). The two must agree on
// the frontmatter shape — both read `slug`/`title`/`tagline` from `--- … ---`.
/** Pull `slug`/`title`/`tagline` from a lesson's `--- … ---` frontmatter. */
function frontmatter(md: string): LessonMeta | null {
  const block = md.match(/^---\n([\s\S]*?)\n---/);
  if (!block) return null;
  const field = (key: string): string | undefined =>
    block[1].match(new RegExp(`^${key}:\\s*(.+)$`, 'm'))?.[1].trim();
  const slug = field('slug');
  const title = field('title');
  const tagline = field('tagline');
  return slug && title && tagline ? { slug, title, tagline } : null;
}

/** Lesson metadata for every `src/learn/lessons/*.md`, sorted by slug. */
function readLessons(): LessonMeta[] {
  return readdirSync(LESSONS_DIR)
    .filter((f) => f.endsWith('.md'))
    .map((f) => frontmatter(readFileSync(LESSONS_DIR + f, 'utf8')))
    .filter((l): l is LessonMeta => l !== null)
    .sort((a, b) => a.slug.localeCompare(b.slug));
}

/** Rollup inputs (one per lesson) → the generated `learn/<slug>/index.html` shells. */
export function lessonInputs(): Record<string, string> {
  const input: Record<string, string> = {};
  for (const { slug } of readLessons()) input[`learn-${slug}`] = `${LEARN_DIR}${slug}/index.html`;
  return input;
}

const escapeHtml = (s: string): string =>
  s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

function shell({ title, tagline }: LessonMeta): string {
  return `<!doctype html>
<html lang="en" data-theme="light">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <link rel="mask-icon" href="/icon-mono.svg" color="#0e7490" />
    <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="${escapeHtml(tagline)}" />
    <title>${escapeHtml(title)} — Astrolabe</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/learn/main.tsx"></script>
  </body>
</html>
`;
}

/** Write a shell per lesson and prune dirs for lessons that no longer exist. */
export function generateLearnPages(): void {
  const lessons = readLessons();
  const wanted = new Set(lessons.map((l) => l.slug));
  for (const lesson of lessons) {
    mkdirSync(`${LEARN_DIR}${lesson.slug}/`, { recursive: true });
    writeFileSync(`${LEARN_DIR}${lesson.slug}/index.html`, shell(lesson));
  }
  for (const entry of readdirSync(LEARN_DIR, { withFileTypes: true })) {
    if (entry.isDirectory() && !wanted.has(entry.name)) {
      rmSync(`${LEARN_DIR}${entry.name}`, { recursive: true, force: true });
    }
  }
}
