// Shared helpers for walking the `src/content/reference/` tree. Used by
// both /reference (index page) and /reference/[...slug] so the two stay
// in sync: same subdirs, same recursive traversal, same gray-matter
// handling, same caching behavior.

import fs from "fs";
import path from "path";
import matter from "gray-matter";

export const REFERENCE_CONTENT_DIR = path.join(
  process.cwd(),
  "src/content/reference",
);

// Top-level reference categories we index. Anything outside this list is
// ignored (e.g. a stray snippet file at the root).
export const REFERENCE_SUBDIRS = ["components", "hooks"] as const;
export type ReferenceSubdir = (typeof REFERENCE_SUBDIRS)[number];

export type ReferenceItem = {
  /** subdir-relative slug, e.g. `components/chat` or `components/inputs/textarea`. */
  slug: string;
  title: string;
  description?: string;
  category: "Components" | "Hooks";
};

function categoryFor(subdir: ReferenceSubdir): "Components" | "Hooks" {
  return subdir === "components" ? "Components" : "Hooks";
}

/**
 * Recursively collect all `.mdx` files under `dir` and return their paths
 * relative to `dir` (without the `.mdx` extension). Silently skips
 * unreadable subdirectories so a single EACCES doesn't break the build.
 */
function walkMdx(dir: string, prefix: string = ""): string[] {
  let entries: fs.Dirent[];
  try {
    entries = fs.readdirSync(dir, { withFileTypes: true });
  } catch (err) {
    console.error(`[reference-items] Failed to read dir ${dir}:`, err);
    return [];
  }
  const out: string[] = [];
  for (const entry of entries) {
    if (entry.name.startsWith(".")) continue;
    const childAbs = path.join(dir, entry.name);
    const childRel = prefix ? `${prefix}/${entry.name}` : entry.name;
    if (entry.isDirectory()) {
      out.push(...walkMdx(childAbs, childRel));
    } else if (entry.isFile() && entry.name.endsWith(".mdx")) {
      out.push(childRel.replace(/\.mdx$/, ""));
    }
  }
  return out;
}

/**
 * Load items from a single reference subdir, recursing into subfolders.
 * Malformed frontmatter on any file is logged and skipped — we never
 * crash the whole index just because one page has a bad YAML block.
 */
function loadSubdirItems(subdir: ReferenceSubdir): ReferenceItem[] {
  const dir = path.join(REFERENCE_CONTENT_DIR, subdir);
  if (!fs.existsSync(dir)) return [];

  const items: ReferenceItem[] = [];
  for (const relSlug of walkMdx(dir)) {
    const filePath = path.join(dir, `${relSlug}.mdx`);
    let raw: string;
    try {
      raw = fs.readFileSync(filePath, "utf-8");
    } catch (err) {
      console.error(`[reference-items] Failed to read ${filePath}:`, err);
      continue;
    }
    let data: Record<string, unknown> = {};
    try {
      ({ data } = matter(raw));
    } catch (err) {
      console.error(
        `[reference-items] Failed to parse frontmatter in ${filePath}:`,
        err,
      );
      continue;
    }
    const fallbackTitle = relSlug.split("/").pop() ?? relSlug;
    items.push({
      slug: `${subdir}/${relSlug}`,
      title:
        typeof data.title === "string" && data.title.length > 0
          ? data.title
          : fallbackTitle,
      description:
        typeof data.description === "string" ? data.description : undefined,
      category: categoryFor(subdir),
    });
  }
  return items;
}

// In-memory cache — keyed by subdir, rebuilt once per process in prod. In
// dev we skip the cache so MDX edits show up without a server restart.
const __itemsCache = new Map<ReferenceSubdir, ReferenceItem[]>();
function isProd(): boolean {
  return process.env.NODE_ENV === "production";
}

export function loadReferenceItems(subdir: ReferenceSubdir): ReferenceItem[] {
  if (isProd()) {
    const cached = __itemsCache.get(subdir);
    if (cached) return cached;
  }
  const items = loadSubdirItems(subdir);
  if (isProd()) __itemsCache.set(subdir, items);
  return items;
}

export function loadAllReferenceItems(): ReferenceItem[] {
  return REFERENCE_SUBDIRS.flatMap((s) => loadReferenceItems(s));
}

/**
 * For `generateStaticParams`: return every reference page as its Next.js
 * catch-all slug array. Recursive (unlike the previous one-level-only
 * implementation), so subfolder docs like `components/inputs/textarea`
 * are statically generated too.
 */
export function referenceStaticParams(): { slug: string[] }[] {
  return loadAllReferenceItems().map((item) => ({
    slug: item.slug.split("/"),
  }));
}
