import path from "node:path";
import fg from "fast-glob";
import type { CategoryConfig, CheckError, SyncCheckResult } from "./shared";

function shouldExclude(fileName: string, exclusions: RegExp[]): boolean {
  return exclusions.some((pattern) => pattern.test(fileName));
}

/**
 * 1:1 file-based sync check: each source file must have a doc file with the
 * same basename, and vice versa.
 */
export async function runSourceSyncCheck(
  categories: CategoryConfig[],
  cwd: string,
): Promise<SyncCheckResult> {
  const errors: CheckError[] = [];
  let filesChecked = 0;

  for (const category of categories) {
    const sources = await fg(category.sourcePattern, { cwd });
    const docs = await fg(category.docPattern, { cwd });

    const sourceBasenames = new Map<string, string>();
    const docBasenames = new Map<string, string>();

    for (const sourcePath of sources) {
      const fileName = path.basename(sourcePath);
      if (shouldExclude(fileName, category.exclusions)) continue;
      let basename = path.basename(sourcePath, path.extname(sourcePath));
      if (basename.endsWith(".generated")) {
        basename = basename.slice(0, -".generated".length);
      }
      sourceBasenames.set(basename.toLowerCase(), sourcePath);
    }

    for (const docPath of docs) {
      const basename = path.basename(docPath, path.extname(docPath));
      docBasenames.set(basename.toLowerCase(), docPath);
    }

    for (const [basename, sourcePath] of sourceBasenames) {
      if (!docBasenames.has(basename)) {
        errors.push({
          type: "missing-doc",
          category: category.name,
          sourcePath,
          expectedBasename: basename,
        });
      }
    }

    for (const [basename, docPath] of docBasenames) {
      if (!sourceBasenames.has(basename)) {
        errors.push({
          type: "orphaned-doc",
          category: category.name,
          docPath,
          expectedBasename: basename,
        });
      }
    }

    filesChecked += sourceBasenames.size + docBasenames.size;
  }

  return { errors, filesChecked };
}
