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

const CONTENT_DIR = path.join(process.cwd(), "content", "labs");
const CONTENT_EXTENSIONS = [".md", ".mdx"] as const;

export type LabSummary = {
  slug: string;
  title: string;
  description: string;
  fileName: string;
};

export type LabDocument = LabSummary & {
  content: string;
  data: Record<string, unknown>;
};

function toTitleCaseFromSlug(slug: string) {
  return slug.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
}

function getSlugFromFileName(fileName: string) {
  return fileName.replace(/\.(md|mdx)$/i, "");
}

function hasSupportedExtension(fileName: string) {
  return CONTENT_EXTENSIONS.some((ext) => fileName.toLowerCase().endsWith(ext));
}

function extractFirstMarkdownHeading(markdown: string) {
  const match = markdown.match(/^#\s+(.+)$/m);
  return match?.[1]?.trim() ?? null;
}

function extractLabNumber(...candidates: Array<string | null | undefined>) {
  for (const candidate of candidates) {
    if (!candidate) continue;
    const match = candidate.match(/\blab[-\s]+(\d+)\b/i);
    if (match?.[1]) {
      return Number.parseInt(match[1], 10);
    }
  }

  return null;
}

function getLabMetadata(fileName: string) {
  const filePath = path.join(CONTENT_DIR, fileName);
  const source = fs.readFileSync(filePath, "utf8");
  const { content, data } = matter(source);
  const slug = getSlugFromFileName(fileName);
  const headingTitle = extractFirstMarkdownHeading(content);

  const title =
    typeof data.title === "string" && data.title.trim().length > 0
      ? data.title.trim()
      : (headingTitle ?? toTitleCaseFromSlug(slug));

  const description =
    typeof data.description === "string" && data.description.trim().length > 0
      ? data.description.trim()
      : "";

  const explicitOrder =
    typeof data.order === "number" && Number.isFinite(data.order)
      ? data.order
      : null;

  const labNumber = extractLabNumber(title, slug);
  const order = explicitOrder ?? labNumber ?? Number.MAX_SAFE_INTEGER;

  return {
    content,
    data,
    description,
    fileName,
    order,
    slug,
    title,
  };
}

export function listLabFiles() {
  if (!fs.existsSync(CONTENT_DIR)) {
    return [];
  }

  return fs
    .readdirSync(CONTENT_DIR)
    .filter((fileName) => hasSupportedExtension(fileName))
    .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
}

export function getLabSummaries() {
  return listLabFiles()
    .map((fileName) => getLabMetadata(fileName))
    .sort((a, b) => {
      if (a.order !== b.order) {
        return a.order - b.order;
      }

      return a.title.localeCompare(b.title, undefined, { numeric: true });
    })
    .map(({ slug, title, description, fileName }) => {
      return {
        slug,
        title,
        description,
        fileName,
      } satisfies LabSummary;
    });
}

export function getLabDocument(slug: string): LabDocument | null {
  const fileName = listLabFiles().find((candidateFileName) => {
    return getSlugFromFileName(candidateFileName) === slug;
  });

  if (!fileName) {
    return null;
  }

  const { content, data, title, description } = getLabMetadata(fileName);

  return {
    slug,
    title,
    description,
    fileName,
    content,
    data,
  };
}
