#!/usr/bin/env node
// Generate lib/manual.generated.ts — a typed TOC of the docs/manual/ chapters.
// Walks docs/manual/, parses front-matter, extracts title and description, and
// emits a single TS file consumed by the in-app /manual route.
//
// Usage: `npm run manual:build` (also chained into `prebuild`).
//
// Pure Node 20 — no external deps. Mirrors scripts/generate-docs-index.mjs.

import { readdir, readFile, writeFile } from 'node:fs/promises';
import { join, relative, basename, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

const SCRIPT_DIR = fileURLToPath(new URL('.', import.meta.url));
const REPO_ROOT = join(SCRIPT_DIR, '..');
const MANUAL_DIR = join(REPO_ROOT, 'docs', 'manual');
const OUT_PATH = join(REPO_ROOT, 'lib', 'manual.generated.ts');

async function walk(dir) {
  const entries = await readdir(dir, { withFileTypes: true });
  const files = [];
  for (const e of entries) {
    const full = join(dir, e.name);
    if (e.isDirectory()) {
      files.push(...(await walk(full)));
    } else if (e.isFile() && e.name.endsWith('.md')) {
      files.push(full);
    }
  }
  return files;
}

function parseFrontMatter(content) {
  if (!content.startsWith('---\n')) return { data: {}, body: content };
  const end = content.indexOf('\n---\n', 4);
  if (end === -1) return { data: {}, body: content };
  const block = content.slice(4, end);
  const data = {};
  for (const raw of block.split('\n')) {
    const line = raw.trim();
    if (!line || line.startsWith('#')) continue;
    const m = line.match(/^([A-Za-z][\w-]*)\s*:\s*(.*?)\s*$/);
    if (!m) continue;
    let val = m[2];
    if (
      (val.startsWith('"') && val.endsWith('"')) ||
      (val.startsWith("'") && val.endsWith("'"))
    ) {
      val = val.slice(1, -1);
    }
    data[m[1]] = val;
  }
  return { data, body: content.slice(end + 5) };
}

function extractFirstH1(body) {
  const m = body.match(/^#\s+(.+?)\s*$/m);
  return m ? m[1] : null;
}

function extractFirstParagraph(body) {
  // Skip leading H1, then take the first non-heading, non-blank block.
  const lines = body.split('\n');
  let i = 0;
  while (i < lines.length && (lines[i].trim() === '' || lines[i].startsWith('#'))) i++;
  const para = [];
  while (i < lines.length && lines[i].trim() !== '') {
    if (lines[i].startsWith('>') || lines[i].startsWith('|') || lines[i].startsWith('```')) break;
    para.push(lines[i]);
    i++;
  }
  return para.join(' ').replace(/\s+/g, ' ').trim();
}

// docs/manual/01-overview.md → ['01-overview']
// docs/manual/index.md       → []
function fileToSlug(rel) {
  const stripped = rel.replace(/^docs\/manual\//, '').replace(/\.md$/, '');
  if (stripped === 'index') return [];
  return stripped.split('/');
}

function escapeTs(s) {
  return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
}

function escapeBacktick(s) {
  return String(s).replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
}

function stripFrontMatter(content) {
  if (!content.startsWith('---\n')) return content;
  const end = content.indexOf('\n---\n', 4);
  if (end === -1) return content;
  return content.slice(end + 5).replace(/^\s*\n/, '');
}

async function main() {
  const files = (await walk(MANUAL_DIR)).sort();
  const entries = [];

  for (const full of files) {
    const rel = relative(REPO_ROOT, full).split(sep).join('/');
    const content = await readFile(full, 'utf8');
    const { data, body } = parseFrontMatter(content);
    const slug = fileToSlug(rel);
    const title = data.title || extractFirstH1(body) || basename(full, '.md');
    const description = extractFirstParagraph(body) || '';
    const markdown = stripFrontMatter(content);
    entries.push({
      slug,
      title,
      description,
      filePath: rel,
      markdown,
    });
  }

  // Sort: index first, then by filename so numeric prefixes drive order.
  entries.sort((a, b) => {
    if (a.slug.length === 0) return -1;
    if (b.slug.length === 0) return 1;
    return a.filePath.localeCompare(b.filePath);
  });

  const lines = [];
  lines.push('// AUTO-GENERATED by scripts/build-manual.mjs. Do not edit by hand.');
  lines.push('// Run `npm run manual:build` to regenerate.');
  lines.push('');
  lines.push('export type ManualEntry = {');
  lines.push('  slug: readonly string[]');
  lines.push('  title: string');
  lines.push('  description: string');
  lines.push('  filePath: string');
  lines.push('  markdown: string');
  lines.push('}');
  lines.push('');
  lines.push('export const MANUAL_TOC: readonly ManualEntry[] = [');
  for (const e of entries) {
    const slugLit = '[' + e.slug.map((s) => `'${escapeTs(s)}'`).join(', ') + '] as const';
    lines.push('  {');
    lines.push(`    slug: ${slugLit},`);
    lines.push(`    title: '${escapeTs(e.title)}',`);
    lines.push(`    description: '${escapeTs(e.description)}',`);
    lines.push(`    filePath: '${escapeTs(e.filePath)}',`);
    lines.push(`    markdown: \`${escapeBacktick(e.markdown)}\`,`);
    lines.push('  },');
  }
  lines.push('] as const;');
  lines.push('');

  await writeFile(OUT_PATH, lines.join('\n'), 'utf8');
  console.log(`Wrote ${relative(REPO_ROOT, OUT_PATH)} (${entries.length} chapters)`);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
